diff options
Diffstat (limited to 'drivers/net/wireless/rsi')
| -rw-r--r-- | drivers/net/wireless/rsi/Kconfig | 30 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/Makefile | 12 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_core.c | 344 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_debugfs.c | 336 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_mac80211.c | 1009 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_main.c | 295 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_mgmt.c | 1307 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_pkt.c | 196 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_sdio.c | 849 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_sdio_ops.c | 564 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_usb.c | 587 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_91x_usb_ops.c | 177 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_boot_params.h | 126 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_common.h | 87 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_debugfs.h | 48 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_main.h | 218 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_mgmt.h | 286 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_sdio.h | 129 | ||||
| -rw-r--r-- | drivers/net/wireless/rsi/rsi_usb.h | 68 | 
19 files changed, 6668 insertions, 0 deletions
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig new file mode 100644 index 00000000000..35245f994c1 --- /dev/null +++ b/drivers/net/wireless/rsi/Kconfig @@ -0,0 +1,30 @@ +config RSI_91X +	tristate "Redpine Signals Inc 91x WLAN driver support" +	depends on MAC80211 +	---help--- +	  This option enabes support for RSI 1x1 devices. +	  Select M (recommended), if you have a RSI 1x1 wireless module. + +config RSI_DEBUGFS +	bool "Redpine Signals Inc debug support" +	depends on RSI_91X +	default y +	---help--- +	 Say Y, if you would like to enable debug support. This option +	 creates debugfs entries + +config RSI_SDIO +	tristate "Redpine Signals SDIO bus support" +	depends on MMC && RSI_91X +	default m +	---help--- +	  This option enables the SDIO bus support in rsi drivers. +	  Select M (recommended), if you have a RSI 1x1 wireless module. + +config RSI_USB +	tristate "Redpine Signals USB bus support" +	depends on USB && RSI_91X +	default m +	---help--- +	  This option enables the USB bus support in rsi drivers. +	  Select M (recommended), if you have a RSI 1x1 wireless module. diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile new file mode 100644 index 00000000000..25828b69275 --- /dev/null +++ b/drivers/net/wireless/rsi/Makefile @@ -0,0 +1,12 @@ +rsi_91x-y			+= rsi_91x_main.o +rsi_91x-y			+= rsi_91x_core.o +rsi_91x-y			+= rsi_91x_mac80211.o +rsi_91x-y			+= rsi_91x_mgmt.o +rsi_91x-y			+= rsi_91x_pkt.o +rsi_91x-$(CONFIG_RSI_DEBUGFS)	+= rsi_91x_debugfs.o + +rsi_usb-y			+= rsi_91x_usb.o rsi_91x_usb_ops.o +rsi_sdio-y			+= rsi_91x_sdio.o rsi_91x_sdio_ops.o +obj-$(CONFIG_RSI_91X) 		+= rsi_91x.o +obj-$(CONFIG_RSI_SDIO)		+= rsi_sdio.o +obj-$(CONFIG_RSI_USB)		+= rsi_usb.o diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c new file mode 100644 index 00000000000..cf61d6e3eaa --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + */ + +#include "rsi_mgmt.h" +#include "rsi_common.h" + +/** + * rsi_determine_min_weight_queue() - This function determines the queue with + *				      the min weight. + * @common: Pointer to the driver private structure. + * + * Return: q_num: Corresponding queue number. + */ +static u8 rsi_determine_min_weight_queue(struct rsi_common *common) +{ +	struct wmm_qinfo *tx_qinfo = common->tx_qinfo; +	u32 q_len = 0; +	u8 ii = 0; + +	for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { +		q_len = skb_queue_len(&common->tx_queue[ii]); +		if ((tx_qinfo[ii].pkt_contended) && q_len) { +			common->min_weight = tx_qinfo[ii].weight; +			break; +		} +	} +	return ii; +} + +/** + * rsi_recalculate_weights() - This function recalculates the weights + *			       corresponding to each queue. + * @common: Pointer to the driver private structure. + * + * Return: recontend_queue bool variable + */ +static bool rsi_recalculate_weights(struct rsi_common *common) +{ +	struct wmm_qinfo *tx_qinfo = common->tx_qinfo; +	bool recontend_queue = false; +	u8 ii = 0; +	u32 q_len = 0; + +	for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { +		q_len = skb_queue_len(&common->tx_queue[ii]); +		/* Check for the need of contention */ +		if (q_len) { +			if (tx_qinfo[ii].pkt_contended) { +				tx_qinfo[ii].weight = +				((tx_qinfo[ii].weight > common->min_weight) ? +				 tx_qinfo[ii].weight - common->min_weight : 0); +			} else { +				tx_qinfo[ii].pkt_contended = 1; +				tx_qinfo[ii].weight = tx_qinfo[ii].wme_params; +				recontend_queue = true; +			} +		} else { /* No packets so no contention */ +			tx_qinfo[ii].weight = 0; +			tx_qinfo[ii].pkt_contended = 0; +		} +	} + +	return recontend_queue; +} + +/** + * rsi_core_determine_hal_queue() - This function determines the queue from + *				    which packet has to be dequeued. + * @common: Pointer to the driver private structure. + * + * Return: q_num: Corresponding queue number on success. + */ +static u8 rsi_core_determine_hal_queue(struct rsi_common *common) +{ +	bool recontend_queue = false; +	u32 q_len = 0; +	u8 q_num = INVALID_QUEUE; +	u8 ii = 0, min = 0; + +	if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) { +		if (!common->mgmt_q_block) +			q_num = MGMT_SOFT_Q; +		return q_num; +	} + +	if (common->pkt_cnt != 0) { +		--common->pkt_cnt; +		return common->selected_qnum; +	} + +get_queue_num: +	recontend_queue = false; + +	q_num = rsi_determine_min_weight_queue(common); + +	q_len = skb_queue_len(&common->tx_queue[ii]); +	ii = q_num; + +	/* Selecting the queue with least back off */ +	for (; ii < NUM_EDCA_QUEUES; ii++) { +		if (((common->tx_qinfo[ii].pkt_contended) && +		     (common->tx_qinfo[ii].weight < min)) && q_len) { +			min = common->tx_qinfo[ii].weight; +			q_num = ii; +		} +	} + +	if (q_num < NUM_EDCA_QUEUES) +		common->tx_qinfo[q_num].pkt_contended = 0; + +	/* Adjust the back off values for all queues again */ +	recontend_queue = rsi_recalculate_weights(common); + +	q_len = skb_queue_len(&common->tx_queue[q_num]); +	if (!q_len) { +		/* If any queues are freshly contended and the selected queue +		 * doesn't have any packets +		 * then get the queue number again with fresh values +		 */ +		if (recontend_queue) +			goto get_queue_num; + +		q_num = INVALID_QUEUE; +		return q_num; +	} + +	common->selected_qnum = q_num; +	q_len = skb_queue_len(&common->tx_queue[q_num]); + +	switch (common->selected_qnum) { +	case VO_Q: +		if (q_len > MAX_CONTINUOUS_VO_PKTS) +			common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1); +		else +			common->pkt_cnt = --q_len; +		break; + +	case VI_Q: +		if (q_len > MAX_CONTINUOUS_VI_PKTS) +			common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1); +		else +			common->pkt_cnt = --q_len; + +		break; + +	default: +		common->pkt_cnt = 0; +		break; +	} + +	return q_num; +} + +/** + * rsi_core_queue_pkt() - This functions enqueues the packet to the queue + *			  specified by the queue number. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +static void rsi_core_queue_pkt(struct rsi_common *common, +			       struct sk_buff *skb) +{ +	u8 q_num = skb->priority; +	if (q_num >= NUM_SOFT_QUEUES) { +		rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", +			__func__, q_num); +		dev_kfree_skb(skb); +		return; +	} + +	skb_queue_tail(&common->tx_queue[q_num], skb); +} + +/** + * rsi_core_dequeue_pkt() - This functions dequeues the packet from the queue + *			    specified by the queue number. + * @common: Pointer to the driver private structure. + * @q_num: Queue number. + * + * Return: Pointer to sk_buff structure. + */ +static struct sk_buff *rsi_core_dequeue_pkt(struct rsi_common *common, +					    u8 q_num) +{ +	if (q_num >= NUM_SOFT_QUEUES) { +		rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", +			__func__, q_num); +		return NULL; +	} + +	return skb_dequeue(&common->tx_queue[q_num]); +} + +/** + * rsi_core_qos_processor() - This function is used to determine the wmm queue + *			      based on the backoff procedure. Data packets are + *			      dequeued from the selected hal queue and sent to + *			      the below layers. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +void rsi_core_qos_processor(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	struct sk_buff *skb; +	unsigned long tstamp_1, tstamp_2; +	u8 q_num; +	int status; + +	tstamp_1 = jiffies; +	while (1) { +		q_num = rsi_core_determine_hal_queue(common); +		rsi_dbg(DATA_TX_ZONE, +			"%s: Queue number = %d\n", __func__, q_num); + +		if (q_num == INVALID_QUEUE) { +			rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__); +			break; +		} + +		mutex_lock(&common->tx_rxlock); + +		status = adapter->check_hw_queue_status(adapter, q_num); +		if ((status <= 0)) { +			mutex_unlock(&common->tx_rxlock); +			break; +		} + +		if ((q_num < MGMT_SOFT_Q) && +		    ((skb_queue_len(&common->tx_queue[q_num])) <= +		      MIN_DATA_QUEUE_WATER_MARK)) { +			if (ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) +				ieee80211_wake_queue(adapter->hw, +						     WME_AC(q_num)); +		} + +		skb = rsi_core_dequeue_pkt(common, q_num); +		if (skb == NULL) { +			mutex_unlock(&common->tx_rxlock); +			break; +		} + +		if (q_num == MGMT_SOFT_Q) +			status = rsi_send_mgmt_pkt(common, skb); +		else +			status = rsi_send_data_pkt(common, skb); + +		if (status) { +			mutex_unlock(&common->tx_rxlock); +			break; +		} + +		common->tx_stats.total_tx_pkt_send[q_num]++; + +		tstamp_2 = jiffies; +		mutex_unlock(&common->tx_rxlock); + +		if (tstamp_2 > tstamp_1 + (300 * HZ / 1000)) +			schedule(); +	} +} + +/** + * rsi_core_xmit() - This function transmits the packets received from mac80211 + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) +{ +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_tx_info *info; +	struct skb_info *tx_params; +	struct ieee80211_hdr *tmp_hdr = NULL; +	u8 q_num, tid = 0; + +	if ((!skb) || (!skb->len)) { +		rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n", +			__func__); +		goto xmit_fail; +	} +	info = IEEE80211_SKB_CB(skb); +	tx_params = (struct skb_info *)info->driver_data; +	tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; + +	if (common->fsm_state != FSM_MAC_INIT_DONE) { +		rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__); +		goto xmit_fail; +	} + +	if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) || +	    (ieee80211_is_ctl(tmp_hdr->frame_control))) { +		q_num = MGMT_SOFT_Q; +		skb->priority = q_num; +	} else { +		if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { +			tid = (skb->data[24] & IEEE80211_QOS_TID); +			skb->priority = TID_TO_WME_AC(tid); +		} else { +			tid = IEEE80211_NONQOS_TID; +			skb->priority = BE_Q; +		} +		q_num = skb->priority; +		tx_params->tid = tid; +		tx_params->sta_id = 0; +	} + +	if ((q_num != MGMT_SOFT_Q) && +	    ((skb_queue_len(&common->tx_queue[q_num]) + 1) >= +	     DATA_QUEUE_WATER_MARK)) { +		if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) +			ieee80211_stop_queue(adapter->hw, WME_AC(q_num)); +		rsi_set_event(&common->tx_thread.event); +		goto xmit_fail; +	} + +	rsi_core_queue_pkt(common, skb); +	rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thead <===\n", __func__); +	rsi_set_event(&common->tx_thread.event); + +	return; + +xmit_fail: +	rsi_dbg(ERR_ZONE, "%s: Failed to queue packet\n", __func__); +	/* Dropping pkt here */ +	ieee80211_free_txskb(common->priv->hw, skb); +} diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c new file mode 100644 index 00000000000..c466246a323 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -0,0 +1,336 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + */ + +#include "rsi_debugfs.h" +#include "rsi_sdio.h" + +/** + * rsi_sdio_stats_read() - This function returns the sdio status of the driver. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_stats_read(struct seq_file *seq, void *data) +{ +	struct rsi_common *common = seq->private; +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; + +	seq_printf(seq, "total_sdio_interrupts: %d\n", +		   dev->rx_info.sdio_int_counter); +	seq_printf(seq, "sdio_msdu_pending_intr_count: %d\n", +		   dev->rx_info.total_sdio_msdu_pending_intr); +	seq_printf(seq, "sdio_buff_full_count : %d\n", +		   dev->rx_info.buf_full_counter); +	seq_printf(seq, "sdio_buf_semi_full_count %d\n", +		   dev->rx_info.buf_semi_full_counter); +	seq_printf(seq, "sdio_unknown_intr_count: %d\n", +		   dev->rx_info.total_sdio_unknown_intr); +	/* RX Path Stats */ +	seq_printf(seq, "BUFFER FULL STATUS  : %d\n", +		   dev->rx_info.buffer_full); +	seq_printf(seq, "SEMI BUFFER FULL STATUS  : %d\n", +		   dev->rx_info.semi_buffer_full); +	seq_printf(seq, "MGMT BUFFER FULL STATUS  : %d\n", +		   dev->rx_info.mgmt_buffer_full); +	seq_printf(seq, "BUFFER FULL COUNTER  : %d\n", +		   dev->rx_info.buf_full_counter); +	seq_printf(seq, "BUFFER SEMI FULL COUNTER  : %d\n", +		   dev->rx_info.buf_semi_full_counter); +	seq_printf(seq, "MGMT BUFFER FULL COUNTER  : %d\n", +		   dev->rx_info.mgmt_buf_full_counter); + +	return 0; +} + +/** + * rsi_sdio_stats_open() - This funtion calls single open function of seq_file + *			   to open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_sdio_stats_open(struct inode *inode, +			       struct file *file) +{ +	return single_open(file, rsi_sdio_stats_read, inode->i_private); +} + +/** + * rsi_version_read() - This function gives driver and firmware version number. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_version_read(struct seq_file *seq, void *data) +{ +	struct rsi_common *common = seq->private; + +	common->driver_ver.major = 0; +	common->driver_ver.minor = 1; +	common->driver_ver.release_num = 0; +	common->driver_ver.patch_num = 0; +	seq_printf(seq, "Driver : %x.%d.%d.%d\nLMAC   : %d.%d.%d.%d\n", +		   common->driver_ver.major, +		   common->driver_ver.minor, +		   common->driver_ver.release_num, +		   common->driver_ver.patch_num, +		   common->fw_ver.major, +		   common->fw_ver.minor, +		   common->fw_ver.release_num, +		   common->fw_ver.patch_num); +	return 0; +} + +/** + * rsi_version_open() - This funtion calls single open function of seq_file to + *			open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_version_open(struct inode *inode, +				 struct file *file) +{ +	return single_open(file, rsi_version_read, inode->i_private); +} + +/** + * rsi_stats_read() - This function return the status of the driver. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_stats_read(struct seq_file *seq, void *data) +{ +	struct rsi_common *common = seq->private; + +	unsigned char fsm_state[][32] = { +		"FSM_CARD_NOT_READY", +		"FSM_BOOT_PARAMS_SENT", +		"FSM_EEPROM_READ_MAC_ADDR", +		"FSM_RESET_MAC_SENT", +		"FSM_RADIO_CAPS_SENT", +		"FSM_BB_RF_PROG_SENT", +		"FSM_MAC_INIT_DONE" +	}; +	seq_puts(seq, "==> RSI STA DRIVER STATUS <==\n"); +	seq_puts(seq, "DRIVER_FSM_STATE: "); + +	if (common->fsm_state <= FSM_MAC_INIT_DONE) +		seq_printf(seq, "%s", fsm_state[common->fsm_state]); + +	seq_printf(seq, "(%d)\n\n", common->fsm_state); + +	/* Mgmt TX Path Stats */ +	seq_printf(seq, "total_mgmt_pkt_send : %d\n", +		   common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]); +	seq_printf(seq, "total_mgmt_pkt_queued : %d\n", +		   skb_queue_len(&common->tx_queue[4])); +	seq_printf(seq, "total_mgmt_pkt_freed  : %d\n", +		   common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]); + +	/* Data TX Path Stats */ +	seq_printf(seq, "total_data_vo_pkt_send: %8d\t", +		   common->tx_stats.total_tx_pkt_send[VO_Q]); +	seq_printf(seq, "total_data_vo_pkt_queued:  %8d\t", +		   skb_queue_len(&common->tx_queue[0])); +	seq_printf(seq, "total_vo_pkt_freed: %8d\n", +		   common->tx_stats.total_tx_pkt_freed[VO_Q]); +	seq_printf(seq, "total_data_vi_pkt_send: %8d\t", +		   common->tx_stats.total_tx_pkt_send[VI_Q]); +	seq_printf(seq, "total_data_vi_pkt_queued:  %8d\t", +		   skb_queue_len(&common->tx_queue[1])); +	seq_printf(seq, "total_vi_pkt_freed: %8d\n", +		   common->tx_stats.total_tx_pkt_freed[VI_Q]); +	seq_printf(seq,  "total_data_be_pkt_send: %8d\t", +		   common->tx_stats.total_tx_pkt_send[BE_Q]); +	seq_printf(seq, "total_data_be_pkt_queued:  %8d\t", +		   skb_queue_len(&common->tx_queue[2])); +	seq_printf(seq, "total_be_pkt_freed: %8d\n", +		   common->tx_stats.total_tx_pkt_freed[BE_Q]); +	seq_printf(seq, "total_data_bk_pkt_send: %8d\t", +		   common->tx_stats.total_tx_pkt_send[BK_Q]); +	seq_printf(seq, "total_data_bk_pkt_queued:  %8d\t", +		   skb_queue_len(&common->tx_queue[3])); +	seq_printf(seq, "total_bk_pkt_freed: %8d\n", +		   common->tx_stats.total_tx_pkt_freed[BK_Q]); + +	seq_puts(seq, "\n"); +	return 0; +} + +/** + * rsi_stats_open() - This funtion calls single open function of seq_file to + *		      open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_stats_open(struct inode *inode, +			  struct file *file) +{ +	return single_open(file, rsi_stats_read, inode->i_private); +} + +/** + * rsi_debug_zone_read() - This function display the currently enabled debug zones. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_debug_zone_read(struct seq_file *seq, void *data) +{ +	rsi_dbg(FSM_ZONE, "%x: rsi_enabled zone", rsi_zone_enabled); +	seq_printf(seq, "The zones available are %#x\n", +		   rsi_zone_enabled); +	return 0; +} + +/** + * rsi_debug_read() - This funtion calls single open function of seq_file to + *		      open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_debug_read(struct inode *inode, +			  struct file *file) +{ +	return single_open(file, rsi_debug_zone_read, inode->i_private); +} + +/** + * rsi_debug_zone_write() - This function writes into hal queues as per user + *			    requirement. + * @filp: Pointer to the file structure. + * @buff: Pointer to the character buffer. + * @len: Length of the data to be written into buffer. + * @data: Pointer to the data. + * + * Return: len: Number of bytes read. + */ +static ssize_t rsi_debug_zone_write(struct file *filp, +				    const char __user *buff, +				    size_t len, +				    loff_t *data) +{ +	unsigned long dbg_zone; +	int ret; + +	if (!len) +		return 0; + +	ret = kstrtoul_from_user(buff, len, 16, &dbg_zone); + +	if (ret) +		return ret; + +	rsi_zone_enabled = dbg_zone; +	return len; +} + +#define FOPS(fopen) { \ +	.owner = THIS_MODULE, \ +	.open = (fopen), \ +	.read = seq_read, \ +	.llseek = seq_lseek, \ +} + +#define FOPS_RW(fopen, fwrite) { \ +	.owner = THIS_MODULE, \ +	.open = (fopen), \ +	.read = seq_read, \ +	.llseek = seq_lseek, \ +	.write = (fwrite), \ +} + +static const struct rsi_dbg_files dev_debugfs_files[] = { +	{"version", 0644, FOPS(rsi_version_open),}, +	{"stats", 0644, FOPS(rsi_stats_open),}, +	{"debug_zone", 0666, FOPS_RW(rsi_debug_read, rsi_debug_zone_write),}, +	{"sdio_stats", 0644, FOPS(rsi_sdio_stats_open),}, +}; + +/** + * rsi_init_dbgfs() - This function initializes the dbgfs entry. + * @adapter: Pointer to the adapter structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_init_dbgfs(struct rsi_hw *adapter) +{ +	struct rsi_common *common = adapter->priv; +	struct rsi_debugfs *dev_dbgfs; +	char devdir[6]; +	int ii; +	const struct rsi_dbg_files *files; + +	dev_dbgfs = kzalloc(sizeof(*dev_dbgfs), GFP_KERNEL); +	if (!dev_dbgfs) +		return -ENOMEM; + +	adapter->dfsentry = dev_dbgfs; + +	snprintf(devdir, sizeof(devdir), "%s", +		 wiphy_name(adapter->hw->wiphy)); + +	dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL); + +	if (!dev_dbgfs->subdir) { +		kfree(dev_dbgfs); +		return -ENOMEM; +	} + +	for (ii = 0; ii < adapter->num_debugfs_entries; ii++) { +		files = &dev_debugfs_files[ii]; +		dev_dbgfs->rsi_files[ii] = +		debugfs_create_file(files->name, +				    files->perms, +				    dev_dbgfs->subdir, +				    common, +				    &files->fops); +	} +	return 0; +} +EXPORT_SYMBOL_GPL(rsi_init_dbgfs); + +/** + * rsi_remove_dbgfs() - Removes the previously created dbgfs file entries + *			in the reverse order of creation. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_remove_dbgfs(struct rsi_hw *adapter) +{ +	struct rsi_debugfs *dev_dbgfs = adapter->dfsentry; + +	if (!dev_dbgfs) +		return; + +	debugfs_remove_recursive(dev_dbgfs->subdir); +} +EXPORT_SYMBOL_GPL(rsi_remove_dbgfs); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c new file mode 100644 index 00000000000..54aaeb09deb --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -0,0 +1,1009 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + */ + +#include <linux/etherdevice.h> +#include "rsi_debugfs.h" +#include "rsi_mgmt.h" +#include "rsi_common.h" + +static const struct ieee80211_channel rsi_2ghz_channels[] = { +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2412, +	  .hw_value = 1 }, /* Channel 1 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2417, +	  .hw_value = 2 }, /* Channel 2 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2422, +	  .hw_value = 3 }, /* Channel 3 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2427, +	  .hw_value = 4 }, /* Channel 4 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2432, +	  .hw_value = 5 }, /* Channel 5 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2437, +	  .hw_value = 6 }, /* Channel 6 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2442, +	  .hw_value = 7 }, /* Channel 7 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2447, +	  .hw_value = 8 }, /* Channel 8 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2452, +	  .hw_value = 9 }, /* Channel 9 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2457, +	  .hw_value = 10 }, /* Channel 10 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2462, +	  .hw_value = 11 }, /* Channel 11 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2467, +	  .hw_value = 12 }, /* Channel 12 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2472, +	  .hw_value = 13 }, /* Channel 13 */ +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2484, +	  .hw_value = 14 }, /* Channel 14 */ +}; + +static const struct ieee80211_channel rsi_5ghz_channels[] = { +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5180, +	  .hw_value = 36,  }, /* Channel 36 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5200, +	  .hw_value = 40, }, /* Channel 40 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5220, +	  .hw_value = 44, }, /* Channel 44 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5240, +	  .hw_value = 48, }, /* Channel 48 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5260, +	  .hw_value = 52, }, /* Channel 52 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5280, +	  .hw_value = 56, }, /* Channel 56 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5300, +	  .hw_value = 60, }, /* Channel 60 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5320, +	  .hw_value = 64, }, /* Channel 64 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5500, +	  .hw_value = 100, }, /* Channel 100 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5520, +	  .hw_value = 104, }, /* Channel 104 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5540, +	  .hw_value = 108, }, /* Channel 108 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5560, +	  .hw_value = 112, }, /* Channel 112 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5580, +	  .hw_value = 116, }, /* Channel 116 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5600, +	  .hw_value = 120, }, /* Channel 120 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5620, +	  .hw_value = 124, }, /* Channel 124 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5640, +	  .hw_value = 128, }, /* Channel 128 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5660, +	  .hw_value = 132, }, /* Channel 132 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5680, +	  .hw_value = 136, }, /* Channel 136 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5700, +	  .hw_value = 140, }, /* Channel 140 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5745, +	  .hw_value = 149, }, /* Channel 149 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5765, +	  .hw_value = 153, }, /* Channel 153 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5785, +	  .hw_value = 157, }, /* Channel 157 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5805, +	  .hw_value = 161, }, /* Channel 161 */ +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5825, +	  .hw_value = 165, }, /* Channel 165 */ +}; + +struct ieee80211_rate rsi_rates[12] = { +	{ .bitrate = STD_RATE_01  * 5, .hw_value = RSI_RATE_1 }, +	{ .bitrate = STD_RATE_02  * 5, .hw_value = RSI_RATE_2 }, +	{ .bitrate = STD_RATE_5_5 * 5, .hw_value = RSI_RATE_5_5 }, +	{ .bitrate = STD_RATE_11  * 5, .hw_value = RSI_RATE_11 }, +	{ .bitrate = STD_RATE_06  * 5, .hw_value = RSI_RATE_6 }, +	{ .bitrate = STD_RATE_09  * 5, .hw_value = RSI_RATE_9 }, +	{ .bitrate = STD_RATE_12  * 5, .hw_value = RSI_RATE_12 }, +	{ .bitrate = STD_RATE_18  * 5, .hw_value = RSI_RATE_18 }, +	{ .bitrate = STD_RATE_24  * 5, .hw_value = RSI_RATE_24 }, +	{ .bitrate = STD_RATE_36  * 5, .hw_value = RSI_RATE_36 }, +	{ .bitrate = STD_RATE_48  * 5, .hw_value = RSI_RATE_48 }, +	{ .bitrate = STD_RATE_54  * 5, .hw_value = RSI_RATE_54 }, +}; + +const u16 rsi_mcsrates[8] = { +	RSI_RATE_MCS0, RSI_RATE_MCS1, RSI_RATE_MCS2, RSI_RATE_MCS3, +	RSI_RATE_MCS4, RSI_RATE_MCS5, RSI_RATE_MCS6, RSI_RATE_MCS7 +}; + +/** + * rsi_is_cipher_wep() -  This function determines if the cipher is WEP or not. + * @common: Pointer to the driver private structure. + * + * Return: If cipher type is WEP, a value of 1 is returned, else 0. + */ + +bool rsi_is_cipher_wep(struct rsi_common *common) +{ +	if (((common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP104) || +	     (common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP40)) && +	    (!common->secinfo.ptk_cipher)) +		return true; +	else +		return false; +} + +/** + * rsi_register_rates_channels() - This function registers channels and rates. + * @adapter: Pointer to the adapter structure. + * @band: Operating band to be set. + * + * Return: None. + */ +static void rsi_register_rates_channels(struct rsi_hw *adapter, int band) +{ +	struct ieee80211_supported_band *sbands = &adapter->sbands[band]; +	void *channels = NULL; + +	if (band == IEEE80211_BAND_2GHZ) { +		channels = kmalloc(sizeof(rsi_2ghz_channels), GFP_KERNEL); +		memcpy(channels, +		       rsi_2ghz_channels, +		       sizeof(rsi_2ghz_channels)); +		sbands->band = IEEE80211_BAND_2GHZ; +		sbands->n_channels = ARRAY_SIZE(rsi_2ghz_channels); +		sbands->bitrates = rsi_rates; +		sbands->n_bitrates = ARRAY_SIZE(rsi_rates); +	} else { +		channels = kmalloc(sizeof(rsi_5ghz_channels), GFP_KERNEL); +		memcpy(channels, +		       rsi_5ghz_channels, +		       sizeof(rsi_5ghz_channels)); +		sbands->band = IEEE80211_BAND_5GHZ; +		sbands->n_channels = ARRAY_SIZE(rsi_5ghz_channels); +		sbands->bitrates = &rsi_rates[4]; +		sbands->n_bitrates = ARRAY_SIZE(rsi_rates) - 4; +	} + +	sbands->channels = channels; + +	memset(&sbands->ht_cap, 0, sizeof(struct ieee80211_sta_ht_cap)); +	sbands->ht_cap.ht_supported = true; +	sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | +			      IEEE80211_HT_CAP_SGI_20 | +			      IEEE80211_HT_CAP_SGI_40); +	sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; +	sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; +	sbands->ht_cap.mcs.rx_mask[0] = 0xff; +	sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +	/* sbands->ht_cap.mcs.rx_highest = 0x82; */ +} + +/** + * rsi_mac80211_attach() - This function is used to de-initialize the + *			   Mac80211 stack. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_mac80211_detach(struct rsi_hw *adapter) +{ +	struct ieee80211_hw *hw = adapter->hw; + +	if (hw) { +		ieee80211_stop_queues(hw); +		ieee80211_unregister_hw(hw); +		ieee80211_free_hw(hw); +	} + +	rsi_remove_dbgfs(adapter); +} +EXPORT_SYMBOL_GPL(rsi_mac80211_detach); + +/** + * rsi_indicate_tx_status() - This function indicates the transmit status. + * @adapter: Pointer to the adapter structure. + * @skb: Pointer to the socket buffer structure. + * @status: Status + * + * Return: None. + */ +void rsi_indicate_tx_status(struct rsi_hw *adapter, +			    struct sk_buff *skb, +			    int status) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + +	memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + +	if (!status) +		info->flags |= IEEE80211_TX_STAT_ACK; + +	ieee80211_tx_status_irqsafe(adapter->hw, skb); +} + +/** + * rsi_mac80211_tx() - This is the handler that 802.11 module calls for each + *		       transmitted frame.SKB contains the buffer starting + *		       from the IEEE 802.11 header. + * @hw: Pointer to the ieee80211_hw structure. + * @control: Pointer to the ieee80211_tx_control structure + * @skb: Pointer to the socket buffer structure. + * + * Return: None + */ +static void rsi_mac80211_tx(struct ieee80211_hw *hw, +			    struct ieee80211_tx_control *control, +			    struct sk_buff *skb) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	rsi_core_xmit(common, skb); +} + +/** + * rsi_mac80211_start() - This is first handler that 802.11 module calls, since + *			  the driver init is complete by then, just + *			  returns success. + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: 0 as success. + */ +static int rsi_mac80211_start(struct ieee80211_hw *hw) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	common->iface_down = false; +	mutex_unlock(&common->mutex); + +	return 0; +} + +/** + * rsi_mac80211_stop() - This is the last handler that 802.11 module calls. + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: None. + */ +static void rsi_mac80211_stop(struct ieee80211_hw *hw) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	common->iface_down = true; +	mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_add_interface() - This function is called when a netdevice + *				  attached to the hardware is enabled. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * + * Return: ret: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_add_interface(struct ieee80211_hw *hw, +				      struct ieee80211_vif *vif) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; +	int ret = -EOPNOTSUPP; + +	mutex_lock(&common->mutex); +	switch (vif->type) { +	case NL80211_IFTYPE_STATION: +		if (!adapter->sc_nvifs) { +			++adapter->sc_nvifs; +			adapter->vifs[0] = vif; +			ret = rsi_set_vap_capabilities(common, STA_OPMODE); +		} +		break; +	default: +		rsi_dbg(ERR_ZONE, +			"%s: Interface type %d not supported\n", __func__, +			vif->type); +	} +	mutex_unlock(&common->mutex); + +	return ret; +} + +/** + * rsi_mac80211_remove_interface() - This function notifies driver that an + *				     interface is going down. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * + * Return: None. + */ +static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, +					  struct ieee80211_vif *vif) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	if (vif->type == NL80211_IFTYPE_STATION) +		adapter->sc_nvifs--; + +	if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif))) +		adapter->vifs[0] = NULL; +	mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_config() - This function is a handler for configuration + *			   requests. The stack calls this function to + *			   change hardware configuration, e.g., channel. + * @hw: Pointer to the ieee80211_hw structure. + * @changed: Changed flags set. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_config(struct ieee80211_hw *hw, +			       u32 changed) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; +	int status = -EOPNOTSUPP; + +	mutex_lock(&common->mutex); +	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { +		struct ieee80211_channel *curchan = hw->conf.chandef.chan; +		u16 channel = curchan->hw_value; + +		rsi_dbg(INFO_ZONE, +			"%s: Set channel: %d MHz type: %d channel_no %d\n", +			__func__, curchan->center_freq, +			curchan->flags, channel); +		common->band = curchan->band; +		status = rsi_set_channel(adapter->priv, channel); +	} +	mutex_unlock(&common->mutex); + +	return status; +} + +/** + * rsi_get_connected_channel() - This function is used to get the current + *				 connected channel number. + * @adapter: Pointer to the adapter structure. + * + * Return: Current connected AP's channel number is returned. + */ +u16 rsi_get_connected_channel(struct rsi_hw *adapter) +{ +	struct ieee80211_vif *vif = adapter->vifs[0]; +	if (vif) { +		struct ieee80211_bss_conf *bss = &vif->bss_conf; +		struct ieee80211_channel *channel = bss->chandef.chan; +		return channel->hw_value; +	} + +	return 0; +} + +/** + * rsi_mac80211_bss_info_changed() - This function is a handler for config + *				     requests related to BSS parameters that + *				     may vary during BSS's lifespan. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @bss_conf: Pointer to the ieee80211_bss_conf structure. + * @changed: Changed flags set. + * + * Return: None. + */ +static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, +					  struct ieee80211_vif *vif, +					  struct ieee80211_bss_conf *bss_conf, +					  u32 changed) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	if (changed & BSS_CHANGED_ASSOC) { +		rsi_dbg(INFO_ZONE, "%s: Changed Association status: %d\n", +			__func__, bss_conf->assoc); +		rsi_inform_bss_status(common, +				      bss_conf->assoc, +				      bss_conf->bssid, +				      bss_conf->qos, +				      bss_conf->aid); +	} +	mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_conf_filter() - This function configure the device's RX filter. + * @hw: Pointer to the ieee80211_hw structure. + * @changed: Changed flags set. + * @total_flags: Total initial flags set. + * @multicast: Multicast. + * + * Return: None. + */ +static void rsi_mac80211_conf_filter(struct ieee80211_hw *hw, +				     u32 changed_flags, +				     u32 *total_flags, +				     u64 multicast) +{ +	/* Not doing much here as of now */ +	*total_flags &= RSI_SUPP_FILTERS; +} + +/** + * rsi_mac80211_conf_tx() - This function configures TX queue parameters + *			    (EDCF (aifs, cw_min, cw_max), bursting) + *			    for a hardware TX queue. + * @hw: Pointer to the ieee80211_hw structure + * @vif: Pointer to the ieee80211_vif structure. + * @queue: Queue number. + * @params: Pointer to ieee80211_tx_queue_params structure. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, u16 queue, +				const struct ieee80211_tx_queue_params *params) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; +	u8 idx = 0; + +	if (queue >= IEEE80211_NUM_ACS) +		return 0; + +	rsi_dbg(INFO_ZONE, +		"%s: Conf queue %d, aifs: %d, cwmin: %d cwmax: %d, txop: %d\n", +		__func__, queue, params->aifs, +		params->cw_min, params->cw_max, params->txop); + +	mutex_lock(&common->mutex); +	/* Map into the way the f/w expects */ +	switch (queue) { +	case IEEE80211_AC_VO: +		idx = VO_Q; +		break; +	case IEEE80211_AC_VI: +		idx = VI_Q; +		break; +	case IEEE80211_AC_BE: +		idx = BE_Q; +		break; +	case IEEE80211_AC_BK: +		idx = BK_Q; +		break; +	default: +		idx = BE_Q; +		break; +	} + +	memcpy(&common->edca_params[idx], +	       params, +	       sizeof(struct ieee80211_tx_queue_params)); +	mutex_unlock(&common->mutex); + +	return 0; +} + +/** + * rsi_hal_key_config() - This function loads the keys into the firmware. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @key: Pointer to the ieee80211_key_conf structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_hal_key_config(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, +			      struct ieee80211_key_conf *key) +{ +	struct rsi_hw *adapter = hw->priv; +	int status; +	u8 key_type; + +	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) +		key_type = RSI_PAIRWISE_KEY; +	else +		key_type = RSI_GROUP_KEY; + +	rsi_dbg(ERR_ZONE, "%s: Cipher 0x%x key_type: %d key_len: %d\n", +		__func__, key->cipher, key_type, key->keylen); + +	if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) || +	    (key->cipher == WLAN_CIPHER_SUITE_WEP40)) { +		status = rsi_hal_load_key(adapter->priv, +					  key->key, +					  key->keylen, +					  RSI_PAIRWISE_KEY, +					  key->keyidx, +					  key->cipher); +		if (status) +			return status; +	} +	return rsi_hal_load_key(adapter->priv, +				key->key, +				key->keylen, +				key_type, +				key->keyidx, +				key->cipher); +} + +/** + * rsi_mac80211_set_key() - This function sets type of key to be loaded. + * @hw: Pointer to the ieee80211_hw structure. + * @cmd: enum set_key_cmd. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * @key: Pointer to the ieee80211_key_conf structure. + * + * Return: status: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_set_key(struct ieee80211_hw *hw, +				enum set_key_cmd cmd, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta, +				struct ieee80211_key_conf *key) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; +	struct security_info *secinfo = &common->secinfo; +	int status; + +	mutex_lock(&common->mutex); +	switch (cmd) { +	case SET_KEY: +		secinfo->security_enable = true; +		status = rsi_hal_key_config(hw, vif, key); +		if (status) { +			mutex_unlock(&common->mutex); +			return status; +		} + +		if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) +			secinfo->ptk_cipher = key->cipher; +		else +			secinfo->gtk_cipher = key->cipher; + +		key->hw_key_idx = key->keyidx; +		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + +		rsi_dbg(ERR_ZONE, "%s: RSI set_key\n", __func__); +		break; + +	case DISABLE_KEY: +		secinfo->security_enable = false; +		rsi_dbg(ERR_ZONE, "%s: RSI del key\n", __func__); +		memset(key, 0, sizeof(struct ieee80211_key_conf)); +		status = rsi_hal_key_config(hw, vif, key); +		break; + +	default: +		status = -EOPNOTSUPP; +		break; +	} + +	mutex_unlock(&common->mutex); +	return status; +} + +/** + * rsi_mac80211_ampdu_action() - This function selects the AMPDU action for + *				 the corresponding mlme_action flag and + *				 informs the f/w regarding this. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @action: ieee80211_ampdu_mlme_action enum. + * @sta: Pointer to the ieee80211_sta structure. + * @tid: Traffic identifier. + * @ssn: Pointer to ssn value. + * @buf_size: Buffer size (for kernel version > 2.6.38). + * + * Return: status: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, +				     struct ieee80211_vif *vif, +				     enum ieee80211_ampdu_mlme_action action, +				     struct ieee80211_sta *sta, +				     unsigned short tid, +				     unsigned short *ssn, +				     unsigned char buf_size) +{ +	int status = -EOPNOTSUPP; +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; +	u16 seq_no = 0; +	u8 ii = 0; + +	for (ii = 0; ii < RSI_MAX_VIFS; ii++) { +		if (vif == adapter->vifs[ii]) +			break; +	} + +	mutex_lock(&common->mutex); +	rsi_dbg(INFO_ZONE, "%s: AMPDU action %d called\n", __func__, action); +	if (ssn != NULL) +		seq_no = *ssn; + +	switch (action) { +	case IEEE80211_AMPDU_RX_START: +		status = rsi_send_aggregation_params_frame(common, +							   tid, +							   seq_no, +							   buf_size, +							   STA_RX_ADDBA_DONE); +		break; + +	case IEEE80211_AMPDU_RX_STOP: +		status = rsi_send_aggregation_params_frame(common, +							   tid, +							   0, +							   buf_size, +							   STA_RX_DELBA); +		break; + +	case IEEE80211_AMPDU_TX_START: +		common->vif_info[ii].seq_start = seq_no; +		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); +		status = 0; +		break; + +	case IEEE80211_AMPDU_TX_STOP_CONT: +	case IEEE80211_AMPDU_TX_STOP_FLUSH: +	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: +		status = rsi_send_aggregation_params_frame(common, +							   tid, +							   seq_no, +							   buf_size, +							   STA_TX_DELBA); +		if (!status) +			ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); +		break; + +	case IEEE80211_AMPDU_TX_OPERATIONAL: +		status = rsi_send_aggregation_params_frame(common, +							   tid, +							   common->vif_info[ii] +								.seq_start, +							   buf_size, +							   STA_TX_ADDBA_DONE); +		break; + +	default: +		rsi_dbg(ERR_ZONE, "%s: Uknown AMPDU action\n", __func__); +		break; +	} + +	mutex_unlock(&common->mutex); +	return status; +} + +/** + * rsi_mac80211_set_rts_threshold() - This function sets rts threshold value. + * @hw: Pointer to the ieee80211_hw structure. + * @value: Rts threshold value. + * + * Return: 0 on success. + */ +static int rsi_mac80211_set_rts_threshold(struct ieee80211_hw *hw, +					  u32 value) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	common->rts_threshold = value; +	mutex_unlock(&common->mutex); + +	return 0; +} + +/** + * rsi_mac80211_set_rate_mask() - This function sets bitrate_mask to be used. + * @hw: Pointer to the ieee80211_hw structure + * @vif: Pointer to the ieee80211_vif structure. + * @mask: Pointer to the cfg80211_bitrate_mask structure. + * + * Return: 0 on success. + */ +static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw, +				      struct ieee80211_vif *vif, +				      const struct cfg80211_bitrate_mask *mask) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); + +	common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0; + +	if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) { +		common->fixedrate_mask[IEEE80211_BAND_2GHZ] = +			(mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12); +	} else { +		common->fixedrate_mask[IEEE80211_BAND_2GHZ] = +			mask->control[IEEE80211_BAND_2GHZ].legacy; +	} +	mutex_unlock(&common->mutex); + +	return 0; +} + +/** + * rsi_fill_rx_status() - This function fills rx status in + *			  ieee80211_rx_status structure. + * @hw: Pointer to the ieee80211_hw structure. + * @skb: Pointer to the socket buffer structure. + * @common: Pointer to the driver private structure. + * @rxs: Pointer to the ieee80211_rx_status structure. + * + * Return: None. + */ +static void rsi_fill_rx_status(struct ieee80211_hw *hw, +			       struct sk_buff *skb, +			       struct rsi_common *common, +			       struct ieee80211_rx_status *rxs) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct skb_info *rx_params = (struct skb_info *)info->driver_data; +	struct ieee80211_hdr *hdr; +	char rssi = rx_params->rssi; +	u8 hdrlen = 0; +	u8 channel = rx_params->channel; +	s32 freq; + +	hdr = ((struct ieee80211_hdr *)(skb->data)); +	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	memset(info, 0, sizeof(struct ieee80211_tx_info)); + +	rxs->signal = -(rssi); + +	if (channel <= 14) +		rxs->band = IEEE80211_BAND_2GHZ; +	else +		rxs->band = IEEE80211_BAND_5GHZ; + +	freq = ieee80211_channel_to_frequency(channel, rxs->band); + +	if (freq) +		rxs->freq = freq; + +	if (ieee80211_has_protected(hdr->frame_control)) { +		if (rsi_is_cipher_wep(common)) { +			memmove(skb->data + 4, skb->data, hdrlen); +			skb_pull(skb, 4); +		} else { +			memmove(skb->data + 8, skb->data, hdrlen); +			skb_pull(skb, 8); +			rxs->flag |= RX_FLAG_MMIC_STRIPPED; +		} +		rxs->flag |= RX_FLAG_DECRYPTED; +		rxs->flag |= RX_FLAG_IV_STRIPPED; +	} +} + +/** + * rsi_indicate_pkt_to_os() - This function sends recieved packet to mac80211. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +void rsi_indicate_pkt_to_os(struct rsi_common *common, +			    struct sk_buff *skb) +{ +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_hw *hw = adapter->hw; +	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + +	if ((common->iface_down) || (!adapter->sc_nvifs)) { +		dev_kfree_skb(skb); +		return; +	} + +	/* filling in the ieee80211_rx_status flags */ +	rsi_fill_rx_status(hw, skb, common, rx_status); + +	ieee80211_rx_irqsafe(hw, skb); +} + +static void rsi_set_min_rate(struct ieee80211_hw *hw, +			     struct ieee80211_sta *sta, +			     struct rsi_common *common) +{ +	u8 band = hw->conf.chandef.chan->band; +	u8 ii; +	u32 rate_bitmap; +	bool matched = false; + +	common->bitrate_mask[band] = sta->supp_rates[band]; + +	rate_bitmap = (common->fixedrate_mask[band] & sta->supp_rates[band]); + +	if (rate_bitmap & 0xfff) { +		/* Find out the min rate */ +		for (ii = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { +			if (rate_bitmap & BIT(ii)) { +				common->min_rate = rsi_rates[ii].hw_value; +				matched = true; +				break; +			} +		} +	} + +	common->vif_info[0].is_ht = sta->ht_cap.ht_supported; + +	if ((common->vif_info[0].is_ht) && (rate_bitmap >> 12)) { +		for (ii = 0; ii < ARRAY_SIZE(rsi_mcsrates); ii++) { +			if ((rate_bitmap >> 12) & BIT(ii)) { +				common->min_rate = rsi_mcsrates[ii]; +				matched = true; +				break; +			} +		} +	} + +	if (!matched) +		common->min_rate = 0xffff; +} + +/** + * rsi_mac80211_sta_add() - This function notifies driver about a peer getting + *			    connected. + * @hw: pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, +				struct ieee80211_vif *vif, +				struct ieee80211_sta *sta) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); + +	rsi_set_min_rate(hw, sta, common); + +	if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) || +	    (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) { +		common->vif_info[0].sgi = true; +	} + +	if (sta->ht_cap.ht_supported) +		ieee80211_start_tx_ba_session(sta, 0, 0); + +	mutex_unlock(&common->mutex); + +	return 0; +} + +/** + * rsi_mac80211_sta_remove() - This function notifies driver about a peer + *			       getting disconnected. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, +				   struct ieee80211_vif *vif, +				   struct ieee80211_sta *sta) +{ +	struct rsi_hw *adapter = hw->priv; +	struct rsi_common *common = adapter->priv; + +	mutex_lock(&common->mutex); +	/* Resetting all the fields to default values */ +	common->bitrate_mask[IEEE80211_BAND_2GHZ] = 0; +	common->bitrate_mask[IEEE80211_BAND_5GHZ] = 0; +	common->min_rate = 0xffff; +	common->vif_info[0].is_ht = false; +	common->vif_info[0].sgi = false; +	common->vif_info[0].seq_start = 0; +	common->secinfo.ptk_cipher = 0; +	common->secinfo.gtk_cipher = 0; +	mutex_unlock(&common->mutex); + +	return 0; +} + +static struct ieee80211_ops mac80211_ops = { +	.tx = rsi_mac80211_tx, +	.start = rsi_mac80211_start, +	.stop = rsi_mac80211_stop, +	.add_interface = rsi_mac80211_add_interface, +	.remove_interface = rsi_mac80211_remove_interface, +	.config = rsi_mac80211_config, +	.bss_info_changed = rsi_mac80211_bss_info_changed, +	.conf_tx = rsi_mac80211_conf_tx, +	.configure_filter = rsi_mac80211_conf_filter, +	.set_key = rsi_mac80211_set_key, +	.set_rts_threshold = rsi_mac80211_set_rts_threshold, +	.set_bitrate_mask = rsi_mac80211_set_rate_mask, +	.ampdu_action = rsi_mac80211_ampdu_action, +	.sta_add = rsi_mac80211_sta_add, +	.sta_remove = rsi_mac80211_sta_remove, +}; + +/** + * rsi_mac80211_attach() - This function is used to initialize Mac80211 stack. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_mac80211_attach(struct rsi_common *common) +{ +	int status = 0; +	struct ieee80211_hw *hw = NULL; +	struct wiphy *wiphy = NULL; +	struct rsi_hw *adapter = common->priv; +	u8 addr_mask[ETH_ALEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3}; + +	rsi_dbg(INIT_ZONE, "%s: Performing mac80211 attach\n", __func__); + +	hw = ieee80211_alloc_hw(sizeof(struct rsi_hw), &mac80211_ops); +	if (!hw) { +		rsi_dbg(ERR_ZONE, "%s: ieee80211 hw alloc failed\n", __func__); +		return -ENOMEM; +	} + +	wiphy = hw->wiphy; + +	SET_IEEE80211_DEV(hw, adapter->device); + +	hw->priv = adapter; +	adapter->hw = hw; + +	hw->flags = IEEE80211_HW_SIGNAL_DBM | +		    IEEE80211_HW_HAS_RATE_CONTROL | +		    IEEE80211_HW_AMPDU_AGGREGATION | +		    0; + +	hw->queues = MAX_HW_QUEUES; +	hw->extra_tx_headroom = RSI_NEEDED_HEADROOM; + +	hw->max_rates = 1; +	hw->max_rate_tries = MAX_RETRIES; + +	hw->max_tx_aggregation_subframes = 6; +	rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ); +	hw->rate_control_algorithm = "AARF"; + +	SET_IEEE80211_PERM_ADDR(hw, common->mac_addr); +	ether_addr_copy(hw->wiphy->addr_mask, addr_mask); + +	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); +	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; +	wiphy->retry_short = RETRY_SHORT; +	wiphy->retry_long  = RETRY_LONG; +	wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; +	wiphy->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; +	wiphy->flags = 0; + +	wiphy->available_antennas_rx = 1; +	wiphy->available_antennas_tx = 1; +	wiphy->bands[IEEE80211_BAND_2GHZ] = +		&adapter->sbands[IEEE80211_BAND_2GHZ]; + +	status = ieee80211_register_hw(hw); +	if (status) +		return status; + +	return rsi_init_dbgfs(adapter); +} diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c new file mode 100644 index 00000000000..8810862ae82 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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/module.h> +#include <linux/firmware.h> +#include "rsi_mgmt.h" +#include "rsi_common.h" + +u32 rsi_zone_enabled = /* INFO_ZONE | +			INIT_ZONE | +			MGMT_TX_ZONE | +			MGMT_RX_ZONE | +			DATA_TX_ZONE | +			DATA_RX_ZONE | +			FSM_ZONE | +			ISR_ZONE | */ +			ERR_ZONE | +			0; +EXPORT_SYMBOL_GPL(rsi_zone_enabled); + +/** + * rsi_dbg() - This function outputs informational messages. + * @zone: Zone of interest for output message. + * @fmt: printf-style format for output message. + * + * Return: none + */ +void rsi_dbg(u32 zone, const char *fmt, ...) +{ +	struct va_format vaf; +	va_list args; + +	va_start(args, fmt); + +	vaf.fmt = fmt; +	vaf.va = &args; + +	if (zone & rsi_zone_enabled) +		pr_info("%pV", &vaf); +	va_end(args); +} +EXPORT_SYMBOL_GPL(rsi_dbg); + +/** + * rsi_prepare_skb() - This function prepares the skb. + * @common: Pointer to the driver private structure. + * @buffer: Pointer to the packet data. + * @pkt_len: Length of the packet. + * @extended_desc: Extended descriptor. + * + * Return: Successfully skb. + */ +static struct sk_buff *rsi_prepare_skb(struct rsi_common *common, +				       u8 *buffer, +				       u32 pkt_len, +				       u8 extended_desc) +{ +	struct ieee80211_tx_info *info; +	struct skb_info *rx_params; +	struct sk_buff *skb = NULL; +	u8 payload_offset; + +	if (WARN(!pkt_len, "%s: Dummy pkt received", __func__)) +		return NULL; + +	if (pkt_len > (RSI_RCV_BUFFER_LEN * 4)) { +		rsi_dbg(ERR_ZONE, "%s: Pkt size > max rx buf size %d\n", +			__func__, pkt_len); +		pkt_len = RSI_RCV_BUFFER_LEN * 4; +	} + +	pkt_len -= extended_desc; +	skb = dev_alloc_skb(pkt_len + FRAME_DESC_SZ); +	if (skb == NULL) +		return NULL; + +	payload_offset = (extended_desc + FRAME_DESC_SZ); +	skb_put(skb, pkt_len); +	memcpy((skb->data), (buffer + payload_offset), skb->len); + +	info = IEEE80211_SKB_CB(skb); +	rx_params = (struct skb_info *)info->driver_data; +	rx_params->rssi = rsi_get_rssi(buffer); +	rx_params->channel = rsi_get_connected_channel(common->priv); + +	return skb; +} + +/** + * rsi_read_pkt() - This function reads frames from the card. + * @common: Pointer to the driver private structure. + * @rcv_pkt_len: Received pkt length. In case of USB it is 0. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len) +{ +	u8 *frame_desc = NULL, extended_desc = 0; +	u32 index, length = 0, queueno = 0; +	u16 actual_length = 0, offset; +	struct sk_buff *skb = NULL; + +	index = 0; +	do { +		frame_desc = &common->rx_data_pkt[index]; +		actual_length = *(u16 *)&frame_desc[0]; +		offset = *(u16 *)&frame_desc[2]; + +		queueno = rsi_get_queueno(frame_desc, offset); +		length = rsi_get_length(frame_desc, offset); +		extended_desc = rsi_get_extended_desc(frame_desc, offset); + +		switch (queueno) { +		case RSI_WIFI_DATA_Q: +			skb = rsi_prepare_skb(common, +					      (frame_desc + offset), +					      length, +					      extended_desc); +			if (skb == NULL) +				goto fail; + +			rsi_indicate_pkt_to_os(common, skb); +			break; + +		case RSI_WIFI_MGMT_Q: +			rsi_mgmt_pkt_recv(common, (frame_desc + offset)); +			break; + +		default: +			rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n", +				__func__,   queueno); +			goto fail; +		} + +		index  += actual_length; +		rcv_pkt_len -= actual_length; +	} while (rcv_pkt_len > 0); + +	return 0; +fail: +	return -EINVAL; +} +EXPORT_SYMBOL_GPL(rsi_read_pkt); + +/** + * rsi_tx_scheduler_thread() - This function is a kernel thread to send the + *			       packets to the device. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +static void rsi_tx_scheduler_thread(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	u32 timeout = EVENT_WAIT_FOREVER; + +	do { +		if (adapter->determine_event_timeout) +			timeout = adapter->determine_event_timeout(adapter); +		rsi_wait_event(&common->tx_thread.event, timeout); +		rsi_reset_event(&common->tx_thread.event); + +		if (common->init_done) +			rsi_core_qos_processor(common); +	} while (atomic_read(&common->tx_thread.thread_done) == 0); +	complete_and_exit(&common->tx_thread.completion, 0); +} + +/** + * rsi_91x_init() - This function initializes os interface operations. + * @void: Void. + * + * Return: Pointer to the adapter structure on success, NULL on failure . + */ +struct rsi_hw *rsi_91x_init(void) +{ +	struct rsi_hw *adapter = NULL; +	struct rsi_common *common = NULL; +	u8 ii = 0; + +	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); +	if (!adapter) +		return NULL; + +	adapter->priv = kzalloc(sizeof(*common), GFP_KERNEL); +	if (adapter->priv == NULL) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of memory\n", +			__func__); +		kfree(adapter); +		return NULL; +	} else { +		common = adapter->priv; +		common->priv = adapter; +	} + +	for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) +		skb_queue_head_init(&common->tx_queue[ii]); + +	rsi_init_event(&common->tx_thread.event); +	mutex_init(&common->mutex); +	mutex_init(&common->tx_rxlock); + +	if (rsi_create_kthread(common, +			       &common->tx_thread, +			       rsi_tx_scheduler_thread, +			       "Tx-Thread")) { +		rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__); +		goto err; +	} + +	common->init_done = true; +	return adapter; + +err: +	kfree(common); +	kfree(adapter); +	return NULL; +} +EXPORT_SYMBOL_GPL(rsi_91x_init); + +/** + * rsi_91x_deinit() - This function de-intializes os intf operations. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_91x_deinit(struct rsi_hw *adapter) +{ +	struct rsi_common *common = adapter->priv; +	u8 ii; + +	rsi_dbg(INFO_ZONE, "%s: Performing deinit os ops\n", __func__); + +	rsi_kill_thread(&common->tx_thread); + +	for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) +		skb_queue_purge(&common->tx_queue[ii]); + +	common->init_done = false; + +	kfree(common); +	kfree(adapter->rsi_dev); +	kfree(adapter); +} +EXPORT_SYMBOL_GPL(rsi_91x_deinit); + +/** + * rsi_91x_hal_module_init() - This function is invoked when the module is + *			       loaded into the kernel. + *			       It registers the client driver. + * @void: Void. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_91x_hal_module_init(void) +{ +	rsi_dbg(INIT_ZONE, "%s: Module init called\n", __func__); +	return 0; +} + +/** + * rsi_91x_hal_module_exit() - This function is called at the time of + *			       removing/unloading the module. + *			       It unregisters the client driver. + * @void: Void. + * + * Return: None. + */ +static void rsi_91x_hal_module_exit(void) +{ +	rsi_dbg(INIT_ZONE, "%s: Module exit called\n", __func__); +} + +module_init(rsi_91x_hal_module_init); +module_exit(rsi_91x_hal_module_exit); +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Station driver for RSI 91x devices"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c new file mode 100644 index 00000000000..2eefbf159bc --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -0,0 +1,1307 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + */ + +#include <linux/etherdevice.h> +#include "rsi_mgmt.h" +#include "rsi_common.h" + +static struct bootup_params boot_params_20 = { +	.magic_number = cpu_to_le16(0x5aa5), +	.crystal_good_time = 0x0, +	.valid = cpu_to_le32(VALID_20), +	.reserved_for_valids = 0x0, +	.bootup_mode_info = 0x0, +	.digital_loop_back_params = 0x0, +	.rtls_timestamp_en = 0x0, +	.host_spi_intr_cfg = 0x0, +	.device_clk_info = {{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| +					      (TA_PLL_M_VAL_20)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| +							 (PLL960_N_VAL_20)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = cpu_to_le16(BIT(3)), +			.bbp_lmac_clk_reg_val = cpu_to_le16(0x121), +			.umac_clock_reg_config = 0x0, +			.qspi_uart_clock_reg_config = 0x0 +		} +	}, +	{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| +							 (TA_PLL_M_VAL_20)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| +							 (PLL960_N_VAL_20)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = 0x0, +			.bbp_lmac_clk_reg_val = 0x0, +			.umac_clock_reg_config = 0x0, +			.qspi_uart_clock_reg_config = 0x0 +		} +	}, +	{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| +							 (TA_PLL_M_VAL_20)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| +							 (PLL960_N_VAL_20)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = 0x0, +			.bbp_lmac_clk_reg_val = 0x0, +			.umac_clock_reg_config = 0x0, +			.qspi_uart_clock_reg_config = 0x0 +		} +	} }, +	.buckboost_wakeup_cnt = 0x0, +	.pmu_wakeup_wait = 0x0, +	.shutdown_wait_time = 0x0, +	.pmu_slp_clkout_sel = 0x0, +	.wdt_prog_value = 0x0, +	.wdt_soc_rst_delay = 0x0, +	.dcdc_operation_mode = 0x0, +	.soc_reset_wait_cnt = 0x0 +}; + +static struct bootup_params boot_params_40 = { +	.magic_number = cpu_to_le16(0x5aa5), +	.crystal_good_time = 0x0, +	.valid = cpu_to_le32(VALID_40), +	.reserved_for_valids = 0x0, +	.bootup_mode_info = 0x0, +	.digital_loop_back_params = 0x0, +	.rtls_timestamp_en = 0x0, +	.host_spi_intr_cfg = 0x0, +	.device_clk_info = {{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| +							 (TA_PLL_M_VAL_40)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| +							 (PLL960_N_VAL_40)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = cpu_to_le16(0x09), +			.bbp_lmac_clk_reg_val = cpu_to_le16(0x1121), +			.umac_clock_reg_config = cpu_to_le16(0x48), +			.qspi_uart_clock_reg_config = 0x0 +		} +	}, +	{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| +							 (TA_PLL_M_VAL_40)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| +							 (PLL960_N_VAL_40)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = 0x0, +			.bbp_lmac_clk_reg_val = 0x0, +			.umac_clock_reg_config = 0x0, +			.qspi_uart_clock_reg_config = 0x0 +		} +	}, +	{ +		.pll_config_g = { +			.tapll_info_g = { +				.pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| +							 (TA_PLL_M_VAL_40)), +				.pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), +			}, +			.pll960_info_g = { +				.pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| +							 (PLL960_N_VAL_40)), +				.pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), +				.pll_reg_3 = 0x0, +			}, +			.afepll_info_g = { +				.pll_reg = cpu_to_le16(0x9f0), +			} +		}, +		.switch_clk_g = { +			.switch_clk_info = 0x0, +			.bbp_lmac_clk_reg_val = 0x0, +			.umac_clock_reg_config = 0x0, +			.qspi_uart_clock_reg_config = 0x0 +		} +	} }, +	.buckboost_wakeup_cnt = 0x0, +	.pmu_wakeup_wait = 0x0, +	.shutdown_wait_time = 0x0, +	.pmu_slp_clkout_sel = 0x0, +	.wdt_prog_value = 0x0, +	.wdt_soc_rst_delay = 0x0, +	.dcdc_operation_mode = 0x0, +	.soc_reset_wait_cnt = 0x0 +}; + +static u16 mcs[] = {13, 26, 39, 52, 78, 104, 117, 130}; + +/** + * rsi_set_default_parameters() - This function sets default parameters. + * @common: Pointer to the driver private structure. + * + * Return: none + */ +static void rsi_set_default_parameters(struct rsi_common *common) +{ +	common->band = IEEE80211_BAND_2GHZ; +	common->channel_width = BW_20MHZ; +	common->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; +	common->channel = 1; +	common->min_rate = 0xffff; +	common->fsm_state = FSM_CARD_NOT_READY; +	common->iface_down = true; +} + +/** + * rsi_set_contention_vals() - This function sets the contention values for the + *			       backoff procedure. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +static void rsi_set_contention_vals(struct rsi_common *common) +{ +	u8 ii = 0; + +	for (; ii < NUM_EDCA_QUEUES; ii++) { +		common->tx_qinfo[ii].wme_params = +			(((common->edca_params[ii].cw_min / 2) + +			  (common->edca_params[ii].aifs)) * +			  WMM_SHORT_SLOT_TIME + SIFS_DURATION); +		common->tx_qinfo[ii].weight = common->tx_qinfo[ii].wme_params; +		common->tx_qinfo[ii].pkt_contended = 0; +	} +} + +/** + * rsi_send_internal_mgmt_frame() - This function sends management frames to + *				    firmware.Also schedules packet to queue + *				    for transmission. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_send_internal_mgmt_frame(struct rsi_common *common, +					struct sk_buff *skb) +{ +	struct skb_info *tx_params; + +	if (skb == NULL) { +		rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__); +		return -ENOMEM; +	} +	tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; +	tx_params->flags |= INTERNAL_MGMT_PKT; +	skb_queue_tail(&common->tx_queue[MGMT_SOFT_Q], skb); +	rsi_set_event(&common->tx_thread.event); +	return 0; +} + +/** + * rsi_load_radio_caps() - This function is used to send radio capabilities + *			   values to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +static int rsi_load_radio_caps(struct rsi_common *common) +{ +	struct rsi_radio_caps *radio_caps; +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_hw *hw = adapter->hw; +	u16 inx = 0; +	u8 ii; +	u8 radio_id = 0; +	u16 gc[20] = {0xf0, 0xf0, 0xf0, 0xf0, +		      0xf0, 0xf0, 0xf0, 0xf0, +		      0xf0, 0xf0, 0xf0, 0xf0, +		      0xf0, 0xf0, 0xf0, 0xf0, +		      0xf0, 0xf0, 0xf0, 0xf0}; +	struct ieee80211_conf *conf = &hw->conf; +	struct sk_buff *skb; + +	rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__); + +	skb = dev_alloc_skb(sizeof(struct rsi_radio_caps)); + +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_radio_caps)); +	radio_caps = (struct rsi_radio_caps *)skb->data; + +	radio_caps->desc_word[1] = cpu_to_le16(RADIO_CAPABILITIES); +	radio_caps->desc_word[4] = cpu_to_le16(RSI_RF_TYPE << 8); + +	if (common->channel_width == BW_40MHZ) { +		radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ); +		radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ); +		if (common->channel_width) { +			radio_caps->desc_word[5] = +				cpu_to_le16(common->channel_width << 12); +			radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE); +		} + +		if (conf_is_ht40_minus(conf)) { +			radio_caps->desc_word[5] = 0; +			radio_caps->desc_word[5] |= +				cpu_to_le16(LOWER_20_ENABLE); +			radio_caps->desc_word[5] |= +				cpu_to_le16(LOWER_20_ENABLE >> 12); +		} + +		if (conf_is_ht40_plus(conf)) { +			radio_caps->desc_word[5] = 0; +			radio_caps->desc_word[5] |= +				cpu_to_le16(UPPER_20_ENABLE); +			radio_caps->desc_word[5] |= +				cpu_to_le16(UPPER_20_ENABLE >> 12); +		} +	} + +	radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8); + +	for (ii = 0; ii < MAX_HW_QUEUES; ii++) { +		radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(3); +		radio_caps->qos_params[ii].cont_win_max_q = cpu_to_le16(0x3f); +		radio_caps->qos_params[ii].aifsn_val_q = cpu_to_le16(2); +		radio_caps->qos_params[ii].txop_q = 0; +	} + +	for (ii = 0; ii < MAX_HW_QUEUES - 4; ii++) { +		radio_caps->qos_params[ii].cont_win_min_q = +			cpu_to_le16(common->edca_params[ii].cw_min); +		radio_caps->qos_params[ii].cont_win_max_q = +			cpu_to_le16(common->edca_params[ii].cw_max); +		radio_caps->qos_params[ii].aifsn_val_q = +			cpu_to_le16((common->edca_params[ii].aifs) << 8); +		radio_caps->qos_params[ii].txop_q = +			cpu_to_le16(common->edca_params[ii].txop); +	} + +	memcpy(&common->rate_pwr[0], &gc[0], 40); +	for (ii = 0; ii < 20; ii++) +		radio_caps->gcpd_per_rate[inx++] = +			cpu_to_le16(common->rate_pwr[ii]  & 0x00FF); + +	radio_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_radio_caps) - +						FRAME_DESC_SZ) | +					       (RSI_WIFI_MGMT_Q << 12)); + + +	skb_put(skb, (sizeof(struct rsi_radio_caps))); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_mgmt_pkt_to_core() - This function is the entry point for Mgmt module. + * @common: Pointer to the driver private structure. + * @msg: Pointer to received packet. + * @msg_len: Length of the recieved packet. + * @type: Type of recieved packet. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mgmt_pkt_to_core(struct rsi_common *common, +				u8 *msg, +				s32 msg_len, +				u8 type) +{ +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_tx_info *info; +	struct skb_info *rx_params; +	u8 pad_bytes = msg[4]; +	u8 pkt_recv; +	struct sk_buff *skb; +	char *buffer; + +	if (type == RX_DOT11_MGMT) { +		if (!adapter->sc_nvifs) +			return -ENOLINK; + +		msg_len -= pad_bytes; +		if ((msg_len <= 0) || (!msg)) { +			rsi_dbg(MGMT_RX_ZONE, +				"%s: Invalid rx msg of len = %d\n", +				__func__, msg_len); +			return -EINVAL; +		} + +		skb = dev_alloc_skb(msg_len); +		if (!skb) { +			rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n", +				__func__); +			return -ENOMEM; +		} + +		buffer = skb_put(skb, msg_len); + +		memcpy(buffer, +		       (u8 *)(msg +  FRAME_DESC_SZ + pad_bytes), +		       msg_len); + +		pkt_recv = buffer[0]; + +		info = IEEE80211_SKB_CB(skb); +		rx_params = (struct skb_info *)info->driver_data; +		rx_params->rssi = rsi_get_rssi(msg); +		rx_params->channel = rsi_get_channel(msg); +		rsi_indicate_pkt_to_os(common, skb); +	} else { +		rsi_dbg(MGMT_TX_ZONE, "%s: Internal Packet\n", __func__); +	} + +	return 0; +} + +/** + * rsi_hal_send_sta_notify_frame() - This function sends the station notify + *				     frame to firmware. + * @common: Pointer to the driver private structure. + * @opmode: Operating mode of device. + * @notify_event: Notification about station connection. + * @bssid: bssid. + * @qos_enable: Qos is enabled. + * @aid: Aid (unique for all STA). + * + * Return: status: 0 on success, corresponding negative error code on failure. + */ +static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, +					 u8 opmode, +					 u8 notify_event, +					 const unsigned char *bssid, +					 u8 qos_enable, +					 u16 aid) +{ +	struct sk_buff *skb = NULL; +	struct rsi_peer_notify *peer_notify; +	u16 vap_id = 0; +	int status; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending sta notify frame\n", __func__); + +	skb = dev_alloc_skb(sizeof(struct rsi_peer_notify)); + +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_peer_notify)); +	peer_notify = (struct rsi_peer_notify *)skb->data; + +	peer_notify->command = cpu_to_le16(opmode << 1); + +	switch (notify_event) { +	case STA_CONNECTED: +		peer_notify->command |= cpu_to_le16(RSI_ADD_PEER); +		break; +	case STA_DISCONNECTED: +		peer_notify->command |= cpu_to_le16(RSI_DELETE_PEER); +		break; +	default: +		break; +	} + +	peer_notify->command |= cpu_to_le16((aid & 0xfff) << 4); +	ether_addr_copy(peer_notify->mac_addr, bssid); + +	peer_notify->sta_flags = cpu_to_le32((qos_enable) ? 1 : 0); + +	peer_notify->desc_word[0] = +		cpu_to_le16((sizeof(struct rsi_peer_notify) - FRAME_DESC_SZ) | +			    (RSI_WIFI_MGMT_Q << 12)); +	peer_notify->desc_word[1] = cpu_to_le16(PEER_NOTIFY); +	peer_notify->desc_word[7] |= cpu_to_le16(vap_id << 8); + +	skb_put(skb, sizeof(struct rsi_peer_notify)); + +	status = rsi_send_internal_mgmt_frame(common, skb); + +	if (!status && qos_enable) { +		rsi_set_contention_vals(common); +		status = rsi_load_radio_caps(common); +	} +	return status; +} + +/** + * rsi_send_aggregation_params_frame() - This function sends the ampdu + *					 indication frame to firmware. + * @common: Pointer to the driver private structure. + * @tid: traffic identifier. + * @ssn: ssn. + * @buf_size: buffer size. + * @event: notification about station connection. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +int rsi_send_aggregation_params_frame(struct rsi_common *common, +				      u16 tid, +				      u16 ssn, +				      u8 buf_size, +				      u8 event) +{ +	struct sk_buff *skb = NULL; +	struct rsi_mac_frame *mgmt_frame; +	u8 peer_id = 0; + +	skb = dev_alloc_skb(FRAME_DESC_SZ); + +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, FRAME_DESC_SZ); +	mgmt_frame = (struct rsi_mac_frame *)skb->data; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending AMPDU indication frame\n", __func__); + +	mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); +	mgmt_frame->desc_word[1] = cpu_to_le16(AMPDU_IND); + +	if (event == STA_TX_ADDBA_DONE) { +		mgmt_frame->desc_word[4] = cpu_to_le16(ssn); +		mgmt_frame->desc_word[5] = cpu_to_le16(buf_size); +		mgmt_frame->desc_word[7] = +		cpu_to_le16((tid | (START_AMPDU_AGGR << 4) | (peer_id << 8))); +	} else if (event == STA_RX_ADDBA_DONE) { +		mgmt_frame->desc_word[4] = cpu_to_le16(ssn); +		mgmt_frame->desc_word[7] = cpu_to_le16(tid | +						       (START_AMPDU_AGGR << 4) | +						       (RX_BA_INDICATION << 5) | +						       (peer_id << 8)); +	} else if (event == STA_TX_DELBA) { +		mgmt_frame->desc_word[7] = cpu_to_le16(tid | +						       (STOP_AMPDU_AGGR << 4) | +						       (peer_id << 8)); +	} else if (event == STA_RX_DELBA) { +		mgmt_frame->desc_word[7] = cpu_to_le16(tid | +						       (STOP_AMPDU_AGGR << 4) | +						       (RX_BA_INDICATION << 5) | +						       (peer_id << 8)); +	} + +	skb_put(skb, FRAME_DESC_SZ); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_program_bb_rf() - This function starts base band and RF programming. + *			 This is called after initial configurations are done. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +static int rsi_program_bb_rf(struct rsi_common *common) +{ +	struct sk_buff *skb; +	struct rsi_mac_frame *mgmt_frame; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending program BB/RF frame\n", __func__); + +	skb = dev_alloc_skb(FRAME_DESC_SZ); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, FRAME_DESC_SZ); +	mgmt_frame = (struct rsi_mac_frame *)skb->data; + +	mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); +	mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA); +	mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8); + +	if (common->rf_reset) { +		mgmt_frame->desc_word[7] =  cpu_to_le16(RF_RESET_ENABLE); +		rsi_dbg(MGMT_TX_ZONE, "%s: ===> RF RESET REQUEST SENT <===\n", +			__func__); +		common->rf_reset = 0; +	} +	common->bb_rf_prog_count = 1; +	mgmt_frame->desc_word[7] |= cpu_to_le16(PUT_BBP_RESET | +				     BBP_REG_WRITE | (RSI_RF_TYPE << 4)); +	skb_put(skb, FRAME_DESC_SZ); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_set_vap_capabilities() - This function send vap capability to firmware. + * @common: Pointer to the driver private structure. + * @opmode: Operating mode of device. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) +{ +	struct sk_buff *skb = NULL; +	struct rsi_vap_caps *vap_caps; +	u16 vap_id = 0; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__); + +	skb = dev_alloc_skb(sizeof(struct rsi_vap_caps)); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_vap_caps)); +	vap_caps = (struct rsi_vap_caps *)skb->data; + +	vap_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_vap_caps) - +					     FRAME_DESC_SZ) | +					     (RSI_WIFI_MGMT_Q << 12)); +	vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES); +	vap_caps->desc_word[4] = cpu_to_le16(mode | +					     (common->channel_width << 8)); +	vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) | +					     (common->mac_id << 4) | +					     common->radio_id); + +	memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN); +	vap_caps->keep_alive_period = cpu_to_le16(90); +	vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD); + +	vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold); +	vap_caps->default_mgmt_rate = 0; +	if (conf_is_ht40(&common->priv->hw->conf)) { +		vap_caps->default_ctrl_rate = +				cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16); +	} else { +		vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6); +	} +	vap_caps->default_data_rate = 0; +	vap_caps->beacon_interval = cpu_to_le16(200); +	vap_caps->dtim_period = cpu_to_le16(4); + +	skb_put(skb, sizeof(*vap_caps)); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_hal_load_key() - This function is used to load keys within the firmware. + * @common: Pointer to the driver private structure. + * @data: Pointer to the key data. + * @key_len: Key length to be loaded. + * @key_type: Type of key: GROUP/PAIRWISE. + * @key_id: Key index. + * @cipher: Type of cipher used. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_hal_load_key(struct rsi_common *common, +		     u8 *data, +		     u16 key_len, +		     u8 key_type, +		     u8 key_id, +		     u32 cipher) +{ +	struct sk_buff *skb = NULL; +	struct rsi_set_key *set_key; +	u16 key_descriptor = 0; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending load key frame\n", __func__); + +	skb = dev_alloc_skb(sizeof(struct rsi_set_key)); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_set_key)); +	set_key = (struct rsi_set_key *)skb->data; + +	if ((cipher == WLAN_CIPHER_SUITE_WEP40) || +	    (cipher == WLAN_CIPHER_SUITE_WEP104)) { +		key_len += 1; +		key_descriptor |= BIT(2); +		if (key_len >= 13) +			key_descriptor |= BIT(3); +	} else if (cipher != KEY_TYPE_CLEAR) { +		key_descriptor |= BIT(4); +		if (key_type == RSI_PAIRWISE_KEY) +			key_id = 0; +		if (cipher == WLAN_CIPHER_SUITE_TKIP) +			key_descriptor |= BIT(5); +	} +	key_descriptor |= (key_type | BIT(13) | (key_id << 14)); + +	set_key->desc_word[0] = cpu_to_le16((sizeof(struct rsi_set_key) - +					    FRAME_DESC_SZ) | +					    (RSI_WIFI_MGMT_Q << 12)); +	set_key->desc_word[1] = cpu_to_le16(SET_KEY_REQ); +	set_key->desc_word[4] = cpu_to_le16(key_descriptor); + +	if ((cipher == WLAN_CIPHER_SUITE_WEP40) || +	    (cipher == WLAN_CIPHER_SUITE_WEP104)) { +		memcpy(&set_key->key[key_id][1], +		       data, +		       key_len * 2); +	} else { +		memcpy(&set_key->key[0][0], data, key_len); +	} + +	memcpy(set_key->tx_mic_key, &data[16], 8); +	memcpy(set_key->rx_mic_key, &data[24], 8); + +	skb_put(skb, sizeof(struct rsi_set_key)); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/* + * rsi_load_bootup_params() - This function send bootup params to the firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static int rsi_load_bootup_params(struct rsi_common *common) +{ +	struct sk_buff *skb; +	struct rsi_boot_params *boot_params; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending boot params frame\n", __func__); +	skb = dev_alloc_skb(sizeof(struct rsi_boot_params)); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_boot_params)); +	boot_params = (struct rsi_boot_params *)skb->data; + +	rsi_dbg(MGMT_TX_ZONE, "%s:\n", __func__); + +	if (common->channel_width == BW_40MHZ) { +		memcpy(&boot_params->bootup_params, +		       &boot_params_40, +		       sizeof(struct bootup_params)); +		rsi_dbg(MGMT_TX_ZONE, "%s: Packet 40MHZ <=== %d\n", __func__, +			UMAC_CLK_40BW); +		boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40BW); +	} else { +		memcpy(&boot_params->bootup_params, +		       &boot_params_20, +		       sizeof(struct bootup_params)); +		if (boot_params_20.valid != cpu_to_le32(VALID_20)) { +			boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_20BW); +			rsi_dbg(MGMT_TX_ZONE, +				"%s: Packet 20MHZ <=== %d\n", __func__, +				UMAC_CLK_20BW); +		} else { +			boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40MHZ); +			rsi_dbg(MGMT_TX_ZONE, +				"%s: Packet 20MHZ <=== %d\n", __func__, +				UMAC_CLK_40MHZ); +		} +	} + +	/** +	 * Bit{0:11} indicates length of the Packet +	 * Bit{12:15} indicates host queue number +	 */ +	boot_params->desc_word[0] = cpu_to_le16(sizeof(struct bootup_params) | +				    (RSI_WIFI_MGMT_Q << 12)); +	boot_params->desc_word[1] = cpu_to_le16(BOOTUP_PARAMS_REQUEST); + +	skb_put(skb, sizeof(struct rsi_boot_params)); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_send_reset_mac() - This function prepares reset MAC request and sends an + *			  internal management frame to indicate it to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static int rsi_send_reset_mac(struct rsi_common *common) +{ +	struct sk_buff *skb; +	struct rsi_mac_frame *mgmt_frame; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending reset MAC frame\n", __func__); + +	skb = dev_alloc_skb(FRAME_DESC_SZ); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, FRAME_DESC_SZ); +	mgmt_frame = (struct rsi_mac_frame *)skb->data; + +	mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); +	mgmt_frame->desc_word[1] = cpu_to_le16(RESET_MAC_REQ); +	mgmt_frame->desc_word[4] = cpu_to_le16(RETRY_COUNT << 8); + +	skb_put(skb, FRAME_DESC_SZ); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_set_channel() - This function programs the channel. + * @common: Pointer to the driver private structure. + * @channel: Channel value to be set. + * + * Return: 0 on success, corresponding error code on failure. + */ +int rsi_set_channel(struct rsi_common *common, u16 channel) +{ +	struct sk_buff *skb = NULL; +	struct rsi_mac_frame *mgmt_frame; + +	rsi_dbg(MGMT_TX_ZONE, +		"%s: Sending scan req frame\n", __func__); + +	if (common->band == IEEE80211_BAND_5GHZ) { +		if ((channel >= 36) && (channel <= 64)) +			channel = ((channel - 32) / 4); +		else if ((channel > 64) && (channel <= 140)) +			channel = ((channel - 102) / 4) + 8; +		else if (channel >= 149) +			channel = ((channel - 151) / 4) + 18; +		else +			return -EINVAL; +	} else { +		if (channel > 14) { +			rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n", +				__func__, channel, common->band); +			return -EINVAL; +		} +	} + +	skb = dev_alloc_skb(FRAME_DESC_SZ); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, FRAME_DESC_SZ); +	mgmt_frame = (struct rsi_mac_frame *)skb->data; + +	mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); +	mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST); +	mgmt_frame->desc_word[4] = cpu_to_le16(channel); + +	mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET | +					       BBP_REG_WRITE | +					       (RSI_RF_TYPE << 4)); + +	mgmt_frame->desc_word[5] = cpu_to_le16(0x01); + +	if (common->channel_width == BW_40MHZ) +		mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8); + +	common->channel = channel; + +	skb_put(skb, FRAME_DESC_SZ); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_compare() - This function is used to compare two integers + * @a: pointer to the first integer + * @b: pointer to the second integer + * + * Return: 0 if both are equal, -1 if the first is smaller, else 1 + */ +static int rsi_compare(const void *a, const void *b) +{ +	u16 _a = *(const u16 *)(a); +	u16 _b = *(const u16 *)(b); + +	if (_a > _b) +		return -1; + +	if (_a < _b) +		return 1; + +	return 0; +} + +/** + * rsi_map_rates() - This function is used to map selected rates to hw rates. + * @rate: The standard rate to be mapped. + * @offset: Offset that will be returned. + * + * Return: 0 if it is a mcs rate, else 1 + */ +static bool rsi_map_rates(u16 rate, int *offset) +{ +	int kk; +	for (kk = 0; kk < ARRAY_SIZE(rsi_mcsrates); kk++) { +		if (rate == mcs[kk]) { +			*offset = kk; +			return false; +		} +	} + +	for (kk = 0; kk < ARRAY_SIZE(rsi_rates); kk++) { +		if (rate == rsi_rates[kk].bitrate / 5) { +			*offset = kk; +			break; +		} +	} +	return true; +} + +/** + * rsi_send_auto_rate_request() - This function is to set rates for connection + *				  and send autorate request to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static int rsi_send_auto_rate_request(struct rsi_common *common) +{ +	struct sk_buff *skb; +	struct rsi_auto_rate *auto_rate; +	int ii = 0, jj = 0, kk = 0; +	struct ieee80211_hw *hw = common->priv->hw; +	u8 band = hw->conf.chandef.chan->band; +	u8 num_supported_rates = 0; +	u8 rate_offset = 0; +	u32 rate_bitmap = common->bitrate_mask[band]; + +	u16 *selected_rates, min_rate; + +	skb = dev_alloc_skb(sizeof(struct rsi_auto_rate)); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL); +	if (!selected_rates) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n", +			__func__); +		dev_kfree_skb(skb); +		return -ENOMEM; +	} + +	memset(skb->data, 0, sizeof(struct rsi_auto_rate)); +	memset(selected_rates, 0, 2 * RSI_TBL_SZ); + +	auto_rate = (struct rsi_auto_rate *)skb->data; + +	auto_rate->aarf_rssi = cpu_to_le16(((u16)3 << 6) | (u16)(18 & 0x3f)); +	auto_rate->collision_tolerance = cpu_to_le16(3); +	auto_rate->failure_limit = cpu_to_le16(3); +	auto_rate->initial_boundary = cpu_to_le16(3); +	auto_rate->max_threshold_limt = cpu_to_le16(27); + +	auto_rate->desc_word[1] = cpu_to_le16(AUTO_RATE_IND); + +	if (common->channel_width == BW_40MHZ) +		auto_rate->desc_word[7] |= cpu_to_le16(1); + +	if (band == IEEE80211_BAND_2GHZ) +		min_rate = STD_RATE_01; +	else +		min_rate = STD_RATE_06; + +	for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { +		if (rate_bitmap & BIT(ii)) { +			selected_rates[jj++] = (rsi_rates[ii].bitrate / 5); +			rate_offset++; +		} +	} +	num_supported_rates = jj; + +	if (common->vif_info[0].is_ht) { +		for (ii = 0; ii < ARRAY_SIZE(mcs); ii++) +			selected_rates[jj++] = mcs[ii]; +		num_supported_rates += ARRAY_SIZE(mcs); +		rate_offset += ARRAY_SIZE(mcs); +	} + +	if (rate_offset < (RSI_TBL_SZ / 2) - 1) { +		for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) { +			selected_rates[jj++] = min_rate; +			rate_offset++; +		} +	} + +	sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL); + +	/* mapping the rates to RSI rates */ +	for (ii = 0; ii < jj; ii++) { +		if (rsi_map_rates(selected_rates[ii], &kk)) { +			auto_rate->supported_rates[ii] = +				cpu_to_le16(rsi_rates[kk].hw_value); +		} else { +			auto_rate->supported_rates[ii] = +				cpu_to_le16(rsi_mcsrates[kk]); +		} +	} + +	/* loading HT rates in the bottom half of the auto rate table */ +	if (common->vif_info[0].is_ht) { +		if (common->vif_info[0].sgi) +			auto_rate->supported_rates[rate_offset++] = +				cpu_to_le16(RSI_RATE_MCS7_SG); + +		for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1; +		     ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) { +			if (common->vif_info[0].sgi) +				auto_rate->supported_rates[ii++] = +					cpu_to_le16(rsi_mcsrates[kk] | BIT(9)); +			auto_rate->supported_rates[ii] = +				cpu_to_le16(rsi_mcsrates[kk--]); +		} + +		for (; ii < RSI_TBL_SZ; ii++) { +			auto_rate->supported_rates[ii] = +				cpu_to_le16(rsi_mcsrates[0]); +		} +	} + +	auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2); +	auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2); +	auto_rate->desc_word[7] |= cpu_to_le16(0 << 8); +	num_supported_rates *= 2; + +	auto_rate->desc_word[0] = cpu_to_le16((sizeof(*auto_rate) - +					       FRAME_DESC_SZ) | +					       (RSI_WIFI_MGMT_Q << 12)); + +	skb_put(skb, +		sizeof(struct rsi_auto_rate)); +	kfree(selected_rates); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_inform_bss_status() - This function informs about bss status with the + *			     help of sta notify params by sending an internal + *			     management frame to firmware. + * @common: Pointer to the driver private structure. + * @status: Bss status type. + * @bssid: Bssid. + * @qos_enable: Qos is enabled. + * @aid: Aid (unique for all STAs). + * + * Return: None. + */ +void rsi_inform_bss_status(struct rsi_common *common, +			   u8 status, +			   const unsigned char *bssid, +			   u8 qos_enable, +			   u16 aid) +{ +	if (status) { +		rsi_hal_send_sta_notify_frame(common, +					      RSI_IFTYPE_STATION, +					      STA_CONNECTED, +					      bssid, +					      qos_enable, +					      aid); +		if (common->min_rate == 0xffff) +			rsi_send_auto_rate_request(common); +	} else { +		rsi_hal_send_sta_notify_frame(common, +					      RSI_IFTYPE_STATION, +					      STA_DISCONNECTED, +					      bssid, +					      qos_enable, +					      aid); +	} +} + +/** + * rsi_eeprom_read() - This function sends a frame to read the mac address + *		       from the eeprom. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_eeprom_read(struct rsi_common *common) +{ +	struct rsi_mac_frame *mgmt_frame; +	struct sk_buff *skb; + +	rsi_dbg(MGMT_TX_ZONE, "%s: Sending EEPROM read req frame\n", __func__); + +	skb = dev_alloc_skb(FRAME_DESC_SZ); +	if (!skb) { +		rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", +			__func__); +		return -ENOMEM; +	} + +	memset(skb->data, 0, FRAME_DESC_SZ); +	mgmt_frame = (struct rsi_mac_frame *)skb->data; + +	/* FrameType */ +	mgmt_frame->desc_word[1] = cpu_to_le16(EEPROM_READ_TYPE); +	mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); +	/* Number of bytes to read */ +	mgmt_frame->desc_word[3] = cpu_to_le16(ETH_ALEN + +					       WLAN_MAC_MAGIC_WORD_LEN + +					       WLAN_HOST_MODE_LEN + +					       WLAN_FW_VERSION_LEN); +	/* Address to read */ +	mgmt_frame->desc_word[4] = cpu_to_le16(WLAN_MAC_EEPROM_ADDR); + +	skb_put(skb, FRAME_DESC_SZ); + +	return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_handle_ta_confirm_type() - This function handles the confirm frames. + * @common: Pointer to the driver private structure. + * @msg: Pointer to received packet. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_handle_ta_confirm_type(struct rsi_common *common, +				      u8 *msg) +{ +	u8 sub_type = (msg[15] & 0xff); + +	switch (sub_type) { +	case BOOTUP_PARAMS_REQUEST: +		rsi_dbg(FSM_ZONE, "%s: Boot up params confirm received\n", +			__func__); +		if (common->fsm_state == FSM_BOOT_PARAMS_SENT) { +			if (rsi_eeprom_read(common)) { +				common->fsm_state = FSM_CARD_NOT_READY; +				goto out; +			} else { +				common->fsm_state = FSM_EEPROM_READ_MAC_ADDR; +			} +		} else { +			rsi_dbg(ERR_ZONE, +				"%s: Received bootup params cfm in %d state\n", +				 __func__, common->fsm_state); +			return 0; +		} +		break; + +	case EEPROM_READ_TYPE: +		if (common->fsm_state == FSM_EEPROM_READ_MAC_ADDR) { +			if (msg[16] == MAGIC_WORD) { +				u8 offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN +					     + WLAN_MAC_MAGIC_WORD_LEN); +				memcpy(common->mac_addr, +				       &msg[offset], +				       ETH_ALEN); +				memcpy(&common->fw_ver, +				       &msg[offset + ETH_ALEN], +				       sizeof(struct version_info)); + +			} else { +				common->fsm_state = FSM_CARD_NOT_READY; +				break; +			} +			if (rsi_send_reset_mac(common)) +				goto out; +			else +				common->fsm_state = FSM_RESET_MAC_SENT; +		} else { +			rsi_dbg(ERR_ZONE, +				"%s: Received eeprom mac addr in %d state\n", +				__func__, common->fsm_state); +			return 0; +		} +		break; + +	case RESET_MAC_REQ: +		if (common->fsm_state == FSM_RESET_MAC_SENT) { +			rsi_dbg(FSM_ZONE, "%s: Reset MAC cfm received\n", +				__func__); + +			if (rsi_load_radio_caps(common)) +				goto out; +			else +				common->fsm_state = FSM_RADIO_CAPS_SENT; +		} else { +			rsi_dbg(ERR_ZONE, +				"%s: Received reset mac cfm in %d state\n", +				 __func__, common->fsm_state); +			return 0; +		} +		break; + +	case RADIO_CAPABILITIES: +		if (common->fsm_state == FSM_RADIO_CAPS_SENT) { +			common->rf_reset = 1; +			if (rsi_program_bb_rf(common)) { +				goto out; +			} else { +				common->fsm_state = FSM_BB_RF_PROG_SENT; +				rsi_dbg(FSM_ZONE, "%s: Radio cap cfm received\n", +					__func__); +			} +		} else { +			rsi_dbg(ERR_ZONE, +				"%s: Received radio caps cfm in %d state\n", +				 __func__, common->fsm_state); +			return 0; +		} +		break; + +	case BB_PROG_VALUES_REQUEST: +	case RF_PROG_VALUES_REQUEST: +	case BBP_PROG_IN_TA: +		rsi_dbg(FSM_ZONE, "%s: BB/RF cfm received\n", __func__); +		if (common->fsm_state == FSM_BB_RF_PROG_SENT) { +			common->bb_rf_prog_count--; +			if (!common->bb_rf_prog_count) { +				common->fsm_state = FSM_MAC_INIT_DONE; +				return rsi_mac80211_attach(common); +			} +		} else { +			goto out; +		} +		break; + +	default: +		rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n", +			__func__); +		break; +	} +	return 0; +out: +	rsi_dbg(ERR_ZONE, "%s: Unable to send pkt/Invalid frame received\n", +		__func__); +	return -EINVAL; +} + +/** + * rsi_mgmt_pkt_recv() - This function processes the management packets + *			 recieved from the hardware. + * @common: Pointer to the driver private structure. + * @msg: Pointer to the received packet. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) +{ +	s32 msg_len = (le16_to_cpu(*(__le16 *)&msg[0]) & 0x0fff); +	u16 msg_type = (msg[2]); +	int ret; + +	rsi_dbg(FSM_ZONE, "%s: Msg Len: %d, Msg Type: %4x\n", +		__func__, msg_len, msg_type); + +	if (msg_type == TA_CONFIRM_TYPE) { +		return rsi_handle_ta_confirm_type(common, msg); +	} else if (msg_type == CARD_READY_IND) { +		rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n", +			__func__); +		if (common->fsm_state == FSM_CARD_NOT_READY) { +			rsi_set_default_parameters(common); + +			ret = rsi_load_bootup_params(common); +			if (ret) +				return ret; +			else +				common->fsm_state = FSM_BOOT_PARAMS_SENT; +		} else { +			return -EINVAL; +		} +	} else if (msg_type == TX_STATUS_IND) { +		if (msg[15] == PROBEREQ_CONFIRM) { +			common->mgmt_q_block = false; +			rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n", +				__func__); +		} +	} else { +		return rsi_mgmt_pkt_to_core(common, msg, msg_len, msg_type); +	} +	return 0; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c new file mode 100644 index 00000000000..8e48e72bae2 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + */ + +#include "rsi_mgmt.h" + +/** + * rsi_send_data_pkt() - This function sends the recieved data packet from + *			 driver to device. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) +{ +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_hdr *tmp_hdr = NULL; +	struct ieee80211_tx_info *info; +	struct skb_info *tx_params; +	struct ieee80211_bss_conf *bss = NULL; +	int status = -EINVAL; +	u8 ieee80211_size = MIN_802_11_HDR_LEN; +	u8 extnd_size = 0; +	__le16 *frame_desc; +	u16 seq_num = 0; + +	info = IEEE80211_SKB_CB(skb); +	bss = &info->control.vif->bss_conf; +	tx_params = (struct skb_info *)info->driver_data; + +	if (!bss->assoc) +		goto err; + +	tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; +	seq_num = (le16_to_cpu(tmp_hdr->seq_ctrl) >> 4); + +	extnd_size = ((uintptr_t)skb->data & 0x3); + +	if ((FRAME_DESC_SZ + extnd_size) > skb_headroom(skb)) { +		rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); +		status = -ENOSPC; +		goto err; +	} + +	skb_push(skb, (FRAME_DESC_SZ + extnd_size)); +	frame_desc = (__le16 *)&skb->data[0]; +	memset((u8 *)frame_desc, 0, FRAME_DESC_SZ); + +	if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { +		ieee80211_size += 2; +		frame_desc[6] |= cpu_to_le16(BIT(12)); +	} + +	if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && +	    (common->secinfo.security_enable)) { +		if (rsi_is_cipher_wep(common)) +			ieee80211_size += 4; +		else +			ieee80211_size += 8; +		frame_desc[6] |= cpu_to_le16(BIT(15)); +	} + +	frame_desc[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | +				    (RSI_WIFI_DATA_Q << 12)); +	frame_desc[2] = cpu_to_le16((extnd_size) | (ieee80211_size) << 8); + +	if (common->min_rate != 0xffff) { +		/* Send fixed rate */ +		frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE); +		frame_desc[4] = cpu_to_le16(common->min_rate); +	} + +	frame_desc[6] |= cpu_to_le16(seq_num & 0xfff); +	frame_desc[7] = cpu_to_le16(((tx_params->tid & 0xf) << 4) | +				    (skb->priority & 0xf) | +				    (tx_params->sta_id << 8)); + +	status = adapter->host_intf_write_pkt(common->priv, +					      skb->data, +					      skb->len); +	if (status) +		rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", +			__func__); + +err: +	++common->tx_stats.total_tx_pkt_freed[skb->priority]; +	rsi_indicate_tx_status(common->priv, skb, status); +	return status; +} + +/** + * rsi_send_mgmt_pkt() - This functions sends the received management packet + *			 from driver to device. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_send_mgmt_pkt(struct rsi_common *common, +		      struct sk_buff *skb) +{ +	struct rsi_hw *adapter = common->priv; +	struct ieee80211_hdr *wh = NULL; +	struct ieee80211_tx_info *info; +	struct ieee80211_bss_conf *bss = NULL; +	struct skb_info *tx_params; +	int status = -E2BIG; +	__le16 *msg = NULL; +	u8 extnd_size = 0; +	u8 vap_id = 0; + +	info = IEEE80211_SKB_CB(skb); +	tx_params = (struct skb_info *)info->driver_data; +	extnd_size = ((uintptr_t)skb->data & 0x3); + +	if (tx_params->flags & INTERNAL_MGMT_PKT) { +		if ((extnd_size) > skb_headroom(skb)) { +			rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); +			dev_kfree_skb(skb); +			return -ENOSPC; +		} +		skb_push(skb, extnd_size); +		skb->data[extnd_size + 4] = extnd_size; +		status = adapter->host_intf_write_pkt(common->priv, +						      (u8 *)skb->data, +						      skb->len); +		if (status) { +			rsi_dbg(ERR_ZONE, +				"%s: Failed to write the packet\n", __func__); +		} +		dev_kfree_skb(skb); +		return status; +	} + +	bss = &info->control.vif->bss_conf; +	wh = (struct ieee80211_hdr *)&skb->data[0]; + +	if (FRAME_DESC_SZ > skb_headroom(skb)) +		goto err; + +	skb_push(skb, FRAME_DESC_SZ); +	memset(skb->data, 0, FRAME_DESC_SZ); +	msg = (__le16 *)skb->data; + +	if (skb->len > MAX_MGMT_PKT_SIZE) { +		rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); +		goto err; +	} + +	msg[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | +			    (RSI_WIFI_MGMT_Q << 12)); +	msg[1] = cpu_to_le16(TX_DOT11_MGMT); +	msg[2] = cpu_to_le16(MIN_802_11_HDR_LEN << 8); +	msg[3] = cpu_to_le16(RATE_INFO_ENABLE); +	msg[6] = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4); + +	if (wh->addr1[0] & BIT(0)) +		msg[3] |= cpu_to_le16(RSI_BROADCAST_PKT); + +	if (common->band == IEEE80211_BAND_2GHZ) +		msg[4] = cpu_to_le16(RSI_11B_MODE); +	else +		msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE); + +	/* Indicate to firmware to give cfm */ +	if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) { +		msg[1] |= cpu_to_le16(BIT(10)); +		msg[7] = cpu_to_le16(PROBEREQ_CONFIRM); +		common->mgmt_q_block = true; +	} + +	msg[7] |= cpu_to_le16(vap_id << 8); + +	status = adapter->host_intf_write_pkt(common->priv, +					      (u8 *)msg, +					      skb->len); +	if (status) +		rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); + +err: +	rsi_indicate_tx_status(common->priv, skb, status); +	return status; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c new file mode 100644 index 00000000000..46e7af446f0 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -0,0 +1,849 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + * + */ + +#include <linux/module.h> +#include "rsi_sdio.h" +#include "rsi_common.h" + +/** + * rsi_sdio_set_cmd52_arg() - This function prepares cmd 52 read/write arg. + * @rw: Read/write + * @func: function number + * @raw: indicates whether to perform read after write + * @address: address to which to read/write + * @writedata: data to write + * + * Return: argument + */ +static u32 rsi_sdio_set_cmd52_arg(bool rw, +				  u8 func, +				  u8 raw, +				  u32 address, +				  u8 writedata) +{ +	return ((rw & 1) << 31) | ((func & 0x7) << 28) | +		((raw & 1) << 27) | (1 << 26) | +		((address & 0x1FFFF) << 9) | (1 << 8) | +		(writedata & 0xFF); +} + +/** + * rsi_cmd52writebyte() - This function issues cmd52 byte write onto the card. + * @card: Pointer to the mmc_card. + * @address: Address to write. + * @byte: Data to write. + * + * Return: Write status. + */ +static int rsi_cmd52writebyte(struct mmc_card *card, +			      u32 address, +			      u8 byte) +{ +	struct mmc_command io_cmd; +	u32 arg; + +	memset(&io_cmd, 0, sizeof(io_cmd)); +	arg = rsi_sdio_set_cmd52_arg(1, 0, 0, address, byte); +	io_cmd.opcode = SD_IO_RW_DIRECT; +	io_cmd.arg = arg; +	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + +	return mmc_wait_for_cmd(card->host, &io_cmd, 0); +} + +/** + * rsi_cmd52readbyte() - This function issues cmd52 byte read onto the card. + * @card: Pointer to the mmc_card. + * @address: Address to read from. + * @byte: Variable to store read value. + * + * Return: Read status. + */ +static int rsi_cmd52readbyte(struct mmc_card *card, +			     u32 address, +			     u8 *byte) +{ +	struct mmc_command io_cmd; +	u32 arg; +	int err; + +	memset(&io_cmd, 0, sizeof(io_cmd)); +	arg = rsi_sdio_set_cmd52_arg(0, 0, 0, address, 0); +	io_cmd.opcode = SD_IO_RW_DIRECT; +	io_cmd.arg = arg; +	io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + +	err = mmc_wait_for_cmd(card->host, &io_cmd, 0); +	if ((!err) && (byte)) +		*byte =  io_cmd.resp[0] & 0xFF; +	return err; +} + +/** + * rsi_issue_sdiocommand() - This function issues sdio commands. + * @func: Pointer to the sdio_func structure. + * @opcode: Opcode value. + * @arg: Arguments to pass. + * @flags: Flags which are set. + * @resp: Pointer to store response. + * + * Return: err: command status as 0 or -1. + */ +static int rsi_issue_sdiocommand(struct sdio_func *func, +				 u32 opcode, +				 u32 arg, +				 u32 flags, +				 u32 *resp) +{ +	struct mmc_command cmd; +	struct mmc_host *host; +	int err; + +	host = func->card->host; + +	memset(&cmd, 0, sizeof(struct mmc_command)); +	cmd.opcode = opcode; +	cmd.arg = arg; +	cmd.flags = flags; +	err = mmc_wait_for_cmd(host, &cmd, 3); + +	if ((!err) && (resp)) +		*resp = cmd.resp[0]; + +	return err; +} + +/** + * rsi_handle_interrupt() - This function is called upon the occurence + *			    of an interrupt. + * @function: Pointer to the sdio_func structure. + * + * Return: None. + */ +static void rsi_handle_interrupt(struct sdio_func *function) +{ +	struct rsi_hw *adapter = sdio_get_drvdata(function); + +	sdio_release_host(function); +	rsi_interrupt_handler(adapter); +	sdio_claim_host(function); +} + +/** + * rsi_reset_card() - This function resets and re-initializes the card. + * @pfunction: Pointer to the sdio_func structure. + * + * Return: None. + */ +static void rsi_reset_card(struct sdio_func *pfunction) +{ +	int ret = 0; +	int err; +	struct mmc_card *card = pfunction->card; +	struct mmc_host *host = card->host; +	s32 bit = (fls(host->ocr_avail) - 1); +	u8 cmd52_resp; +	u32 clock, resp, i; +	u16 rca; + +	/* Reset 9110 chip */ +	ret = rsi_cmd52writebyte(pfunction->card, +				 SDIO_CCCR_ABORT, +				 (1 << 3)); + +	/* Card will not send any response as it is getting reset immediately +	 * Hence expect a timeout status from host controller +	 */ +	if (ret != -ETIMEDOUT) +		rsi_dbg(ERR_ZONE, "%s: Reset failed : %d\n", __func__, ret); + +	/* Wait for few milli seconds to get rid of residue charges if any */ +	msleep(20); + +	/* Initialize the SDIO card */ +	host->ios.vdd = bit; +	host->ios.chip_select = MMC_CS_DONTCARE; +	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; +	host->ios.power_mode = MMC_POWER_UP; +	host->ios.bus_width = MMC_BUS_WIDTH_1; +	host->ios.timing = MMC_TIMING_LEGACY; +	host->ops->set_ios(host, &host->ios); + +	/* +	 * This delay should be sufficient to allow the power supply +	 * to reach the minimum voltage. +	 */ +	msleep(20); + +	host->ios.clock = host->f_min; +	host->ios.power_mode = MMC_POWER_ON; +	host->ops->set_ios(host, &host->ios); + +	/* +	 * This delay must be at least 74 clock sizes, or 1 ms, or the +	 * time required to reach a stable voltage. +	 */ +	msleep(20); + +	/* Issue CMD0. Goto idle state */ +	host->ios.chip_select = MMC_CS_HIGH; +	host->ops->set_ios(host, &host->ios); +	msleep(20); +	err = rsi_issue_sdiocommand(pfunction, +				    MMC_GO_IDLE_STATE, +				    0, +				    (MMC_RSP_NONE | MMC_CMD_BC), +				    NULL); +	host->ios.chip_select = MMC_CS_DONTCARE; +	host->ops->set_ios(host, &host->ios); +	msleep(20); +	host->use_spi_crc = 0; + +	if (err) +		rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err); + +	if (!host->ocr_avail) { +		/* Issue CMD5, arg = 0 */ +		err = rsi_issue_sdiocommand(pfunction, +					    SD_IO_SEND_OP_COND, +					    0, +					    (MMC_RSP_R4 | MMC_CMD_BCR), +					    &resp); +		if (err) +			rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", +				__func__, err); +		host->ocr_avail = resp; +	} + +	/* Issue CMD5, arg = ocr. Wait till card is ready  */ +	for (i = 0; i < 100; i++) { +		err = rsi_issue_sdiocommand(pfunction, +					    SD_IO_SEND_OP_COND, +					    host->ocr_avail, +					    (MMC_RSP_R4 | MMC_CMD_BCR), +					    &resp); +		if (err) { +			rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", +				__func__, err); +			break; +		} + +		if (resp & MMC_CARD_BUSY) +			break; +		msleep(20); +	} + +	if ((i == 100) || (err)) { +		rsi_dbg(ERR_ZONE, "%s: card in not ready : %d %d\n", +			__func__, i, err); +		return; +	} + +	/* Issue CMD3, get RCA */ +	err = rsi_issue_sdiocommand(pfunction, +				    SD_SEND_RELATIVE_ADDR, +				    0, +				    (MMC_RSP_R6 | MMC_CMD_BCR), +				    &resp); +	if (err) { +		rsi_dbg(ERR_ZONE, "%s: CMD3 failed : %d\n", __func__, err); +		return; +	} +	rca = resp >> 16; +	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; +	host->ops->set_ios(host, &host->ios); + +	/* Issue CMD7, select card  */ +	err = rsi_issue_sdiocommand(pfunction, +				    MMC_SELECT_CARD, +				    (rca << 16), +				    (MMC_RSP_R1 | MMC_CMD_AC), +				    NULL); +	if (err) { +		rsi_dbg(ERR_ZONE, "%s: CMD7 failed : %d\n", __func__, err); +		return; +	} + +	/* Enable high speed */ +	if (card->host->caps & MMC_CAP_SD_HIGHSPEED) { +		rsi_dbg(ERR_ZONE, "%s: Set high speed mode\n", __func__); +		err = rsi_cmd52readbyte(card, SDIO_CCCR_SPEED, &cmd52_resp); +		if (err) { +			rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n", +				__func__, err); +		} else { +			err = rsi_cmd52writebyte(card, +						 SDIO_CCCR_SPEED, +						 (cmd52_resp | SDIO_SPEED_EHS)); +			if (err) { +				rsi_dbg(ERR_ZONE, +					"%s: CCR speed regwrite failed %d\n", +					__func__, err); +				return; +			} +			host->ios.timing = MMC_TIMING_SD_HS; +			host->ops->set_ios(host, &host->ios); +		} +	} + +	/* Set clock */ +	if (mmc_card_hs(card)) +		clock = 50000000; +	else +		clock = card->cis.max_dtr; + +	if (clock > host->f_max) +		clock = host->f_max; + +	host->ios.clock = clock; +	host->ops->set_ios(host, &host->ios); + +	if (card->host->caps & MMC_CAP_4_BIT_DATA) { +		/* CMD52: Set bus width & disable card detect resistor */ +		err = rsi_cmd52writebyte(card, +					 SDIO_CCCR_IF, +					 (SDIO_BUS_CD_DISABLE | +					  SDIO_BUS_WIDTH_4BIT)); +		if (err) { +			rsi_dbg(ERR_ZONE, "%s: Set bus mode failed : %d\n", +				__func__, err); +			return; +		} +		host->ios.bus_width = MMC_BUS_WIDTH_4; +		host->ops->set_ios(host, &host->ios); +	} +} + +/** + * rsi_setclock() - This function sets the clock frequency. + * @adapter: Pointer to the adapter structure. + * @freq: Clock frequency. + * + * Return: None. + */ +static void rsi_setclock(struct rsi_hw *adapter, u32 freq) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	struct mmc_host *host = dev->pfunction->card->host; +	u32 clock; + +	clock = freq * 1000; +	if (clock > host->f_max) +		clock = host->f_max; +	host->ios.clock = clock; +	host->ops->set_ios(host, &host->ios); +} + +/** + * rsi_setblocklength() - This function sets the host block length. + * @adapter: Pointer to the adapter structure. + * @length: Block length to be set. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_setblocklength(struct rsi_hw *adapter, u32 length) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	int status; +	rsi_dbg(INIT_ZONE, "%s: Setting the block length\n", __func__); + +	status = sdio_set_block_size(dev->pfunction, length); +	dev->pfunction->max_blksize = 256; + +	rsi_dbg(INFO_ZONE, +		"%s: Operational blk length is %d\n", __func__, length); +	return status; +} + +/** + * rsi_setupcard() - This function queries and sets the card's features. + * @adapter: Pointer to the adapter structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_setupcard(struct rsi_hw *adapter) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	int status = 0; + +	rsi_setclock(adapter, 50000); + +	dev->tx_blk_size = 256; +	status = rsi_setblocklength(adapter, dev->tx_blk_size); +	if (status) +		rsi_dbg(ERR_ZONE, +			"%s: Unable to set block length\n", __func__); +	return status; +} + +/** + * rsi_sdio_read_register() - This function reads one byte of information + *			      from a register. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that stores the data read. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_read_register(struct rsi_hw *adapter, +			   u32 addr, +			   u8 *data) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u8 fun_num = 0; +	int status; + +	sdio_claim_host(dev->pfunction); + +	if (fun_num == 0) +		*data = sdio_f0_readb(dev->pfunction, addr, &status); +	else +		*data = sdio_readb(dev->pfunction, addr, &status); + +	sdio_release_host(dev->pfunction); + +	return status; +} + +/** + * rsi_sdio_write_register() - This function writes one byte of information + *			       into a register. + * @adapter: Pointer to the adapter structure. + * @function: Function Number. + * @addr: Address of the register. + * @data: Pointer to the data tha has to be written. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_write_register(struct rsi_hw *adapter, +			    u8 function, +			    u32 addr, +			    u8 *data) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	int status = 0; + +	sdio_claim_host(dev->pfunction); + +	if (function == 0) +		sdio_f0_writeb(dev->pfunction, *data, addr, &status); +	else +		sdio_writeb(dev->pfunction, *data, addr, &status); + +	sdio_release_host(dev->pfunction); + +	return status; +} + +/** + * rsi_sdio_ack_intr() - This function acks the interrupt received. + * @adapter: Pointer to the adapter structure. + * @int_bit: Interrupt bit to write into register. + * + * Return: None. + */ +void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit) +{ +	int status; +	status = rsi_sdio_write_register(adapter, +					 1, +					 (SDIO_FUN1_INTR_CLR_REG | +					  RSI_SD_REQUEST_MASTER), +					 &int_bit); +	if (status) +		rsi_dbg(ERR_ZONE, "%s: unable to send ack\n", __func__); +} + + + +/** + * rsi_sdio_read_register_multiple() - This function read multiple bytes of + *				       information from the SD card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @count: Number of multiple bytes to be read. + * @data: Pointer to the read data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_read_register_multiple(struct rsi_hw *adapter, +					   u32 addr, +					   u32 count, +					   u8 *data) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u32 status; + +	sdio_claim_host(dev->pfunction); + +	status =  sdio_readsb(dev->pfunction, data, addr, count); + +	sdio_release_host(dev->pfunction); + +	if (status != 0) +		rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 read failed\n", __func__); +	return status; +} + +/** + * rsi_sdio_write_register_multiple() - This function writes multiple bytes of + *					information to the SD card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, +				     u32 addr, +				     u8 *data, +				     u32 count) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	int status; + +	if (dev->write_fail > 1) { +		rsi_dbg(ERR_ZONE, "%s: Stopping card writes\n", __func__); +		return 0; +	} else if (dev->write_fail == 1) { +		/** +		 * Assuming it is a CRC failure, we want to allow another +		 *  card write +		 */ +		rsi_dbg(ERR_ZONE, "%s: Continue card writes\n", __func__); +		dev->write_fail++; +	} + +	sdio_claim_host(dev->pfunction); + +	status = sdio_writesb(dev->pfunction, addr, data, count); + +	sdio_release_host(dev->pfunction); + +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 write failed %d\n", +			__func__, status); +		dev->write_fail = 2; +	} else { +		memcpy(dev->prev_desc, data, FRAME_DESC_SZ); +	} +	return status; +} + +/** + * rsi_sdio_host_intf_write_pkt() - This function writes the packet to device. + * @adapter: Pointer to the adapter structure. + * @pkt: Pointer to the data to be written on to the device. + * @len: length of the data to be written on to the device. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_host_intf_write_pkt(struct rsi_hw *adapter, +					u8 *pkt, +					u32 len) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u32 block_size = dev->tx_blk_size; +	u32 num_blocks, address, length; +	u32 queueno; +	int status; + +	queueno = ((pkt[1] >> 4) & 0xf); + +	num_blocks = len / block_size; + +	if (len % block_size) +		num_blocks++; + +	address = (num_blocks * block_size | (queueno << 12)); +	length  = num_blocks * block_size; + +	status = rsi_sdio_write_register_multiple(adapter, +						  address, +						  (u8 *)pkt, +						  length); +	if (status) +		rsi_dbg(ERR_ZONE, "%s: Unable to write onto the card: %d\n", +			__func__, status); +	rsi_dbg(DATA_TX_ZONE, "%s: Successfully written onto card\n", __func__); +	return status; +} + +/** + * rsi_sdio_host_intf_read_pkt() - This function reads the packet +				   from the device. + * @adapter: Pointer to the adapter data structure. + * @pkt: Pointer to the packet data to be read from the the device. + * @length: Length of the data to be read from the device. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, +				u8 *pkt, +				u32 length) +{ +	int status = -EINVAL; + +	if (!length) { +		rsi_dbg(ERR_ZONE, "%s: Pkt size is zero\n", __func__); +		return status; +	} + +	status = rsi_sdio_read_register_multiple(adapter, +						 length, +						 length, /*num of bytes*/ +						 (u8 *)pkt); + +	if (status) +		rsi_dbg(ERR_ZONE, "%s: Failed to read frame: %d\n", __func__, +			status); +	return status; +} + +/** + * rsi_init_sdio_interface() - This function does init specific to SDIO. + * + * @adapter: Pointer to the adapter data structure. + * @pkt: Pointer to the packet data to be read from the the device. + * + * Return: 0 on success, -1 on failure. + */ + +static int rsi_init_sdio_interface(struct rsi_hw *adapter, +				   struct sdio_func *pfunction) +{ +	struct rsi_91x_sdiodev *rsi_91x_dev; +	int status = -ENOMEM; + +	rsi_91x_dev = kzalloc(sizeof(*rsi_91x_dev), GFP_KERNEL); +	if (!rsi_91x_dev) +		return status; + +	adapter->rsi_dev = rsi_91x_dev; + +	sdio_claim_host(pfunction); + +	pfunction->enable_timeout = 100; +	status = sdio_enable_func(pfunction); +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Failed to enable interface\n", __func__); +		sdio_release_host(pfunction); +		return status; +	} + +	rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); + +	rsi_91x_dev->pfunction = pfunction; +	adapter->device = &pfunction->dev; + +	sdio_set_drvdata(pfunction, adapter); + +	status = rsi_setupcard(adapter); +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Failed to setup card\n", __func__); +		goto fail; +	} + +	rsi_dbg(INIT_ZONE, "%s: Setup card succesfully\n", __func__); + +	status = rsi_init_sdio_slave_regs(adapter); +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Failed to init slave regs\n", __func__); +		goto fail; +	} +	sdio_release_host(pfunction); + +	adapter->host_intf_write_pkt = rsi_sdio_host_intf_write_pkt; +	adapter->host_intf_read_pkt = rsi_sdio_host_intf_read_pkt; +	adapter->determine_event_timeout = rsi_sdio_determine_event_timeout; +	adapter->check_hw_queue_status = rsi_sdio_read_buffer_status_register; + +#ifdef CONFIG_RSI_DEBUGFS +	adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES; +#endif +	return status; +fail: +	sdio_disable_func(pfunction); +	sdio_release_host(pfunction); +	return status; +} + +/** + * rsi_probe() - This function is called by kernel when the driver provided + *		 Vendor and device IDs are matched. All the initialization + *		 work is done here. + * @pfunction: Pointer to the sdio_func structure. + * @id: Pointer to sdio_device_id structure. + * + * Return: 0 on success, 1 on failure. + */ +static int rsi_probe(struct sdio_func *pfunction, +		     const struct sdio_device_id *id) +{ +	struct rsi_hw *adapter; + +	rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); + +	adapter = rsi_91x_init(); +	if (!adapter) { +		rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", +			__func__); +		return 1; +	} + +	if (rsi_init_sdio_interface(adapter, pfunction)) { +		rsi_dbg(ERR_ZONE, "%s: Failed to init sdio interface\n", +			__func__); +		goto fail; +	} + +	if (rsi_sdio_device_init(adapter->priv)) { +		rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__); +		sdio_claim_host(pfunction); +		sdio_disable_func(pfunction); +		sdio_release_host(pfunction); +		goto fail; +	} + +	sdio_claim_host(pfunction); +	if (sdio_claim_irq(pfunction, rsi_handle_interrupt)) { +		rsi_dbg(ERR_ZONE, "%s: Failed to request IRQ\n", __func__); +		sdio_release_host(pfunction); +		goto fail; +	} + +	sdio_release_host(pfunction); +	rsi_dbg(INIT_ZONE, "%s: Registered Interrupt handler\n", __func__); + +	return 0; +fail: +	rsi_91x_deinit(adapter); +	rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); +	return 1; +} + +/** + * rsi_disconnect() - This function performs the reverse of the probe function. + * @pfunction: Pointer to the sdio_func structure. + * + * Return: void. + */ +static void rsi_disconnect(struct sdio_func *pfunction) +{ +	struct rsi_hw *adapter = sdio_get_drvdata(pfunction); +	struct rsi_91x_sdiodev *dev; + +	if (!adapter) +		return; + +	dev = (struct rsi_91x_sdiodev *)adapter->rsi_dev; + +	dev->write_fail = 2; +	rsi_mac80211_detach(adapter); + +	sdio_claim_host(pfunction); +	sdio_release_irq(pfunction); +	sdio_disable_func(pfunction); +	rsi_91x_deinit(adapter); +	/* Resetting to take care of the case, where-in driver is re-loaded */ +	rsi_reset_card(pfunction); +	sdio_release_host(pfunction); +} + +#ifdef CONFIG_PM +static int rsi_suspend(struct device *dev) +{ +	/* Not yet implemented */ +	return -ENOSYS; +} + +static int rsi_resume(struct device *dev) +{ +	/* Not yet implemented */ +	return -ENOSYS; +} + +static const struct dev_pm_ops rsi_pm_ops = { +	.suspend = rsi_suspend, +	.resume = rsi_resume, +}; +#endif + +static const struct sdio_device_id rsi_dev_table[] =  { +	{ SDIO_DEVICE(0x303, 0x100) }, +	{ SDIO_DEVICE(0x041B, 0x0301) }, +	{ SDIO_DEVICE(0x041B, 0x0201) }, +	{ SDIO_DEVICE(0x041B, 0x9330) }, +	{ /* Blank */}, +}; + +static struct sdio_driver rsi_driver = { +	.name       = "RSI-SDIO WLAN", +	.probe      = rsi_probe, +	.remove     = rsi_disconnect, +	.id_table   = rsi_dev_table, +#ifdef CONFIG_PM +	.drv = { +		.pm = &rsi_pm_ops, +	} +#endif +}; + +/** + * rsi_module_init() - This function registers the sdio module. + * @void: Void. + * + * Return: 0 on success. + */ +static int rsi_module_init(void) +{ +	sdio_register_driver(&rsi_driver); +	rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); +	return 0; +} + +/** + * rsi_module_exit() - This function unregisters the sdio module. + * @void: Void. + * + * Return: None. + */ +static void rsi_module_exit(void) +{ +	sdio_unregister_driver(&rsi_driver); +	rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__); +} + +module_init(rsi_module_init); +module_exit(rsi_module_exit); + +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Common SDIO layer for RSI drivers"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_DEVICE_TABLE(sdio, rsi_dev_table); +MODULE_FIRMWARE(FIRMWARE_RSI9113); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c new file mode 100644 index 00000000000..20d11ccfffe --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -0,0 +1,564 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + * + */ + +#include <linux/firmware.h> +#include "rsi_sdio.h" +#include "rsi_common.h" + +/** + * rsi_sdio_master_access_msword() - This function sets the AHB master access + *				     MS word in the SDIO slave registers. + * @adapter: Pointer to the adapter structure. + * @ms_word: ms word need to be initialized. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_sdio_master_access_msword(struct rsi_hw *adapter, +					 u16 ms_word) +{ +	u8 byte; +	u8 function = 0; +	int status = 0; + +	byte = (u8)(ms_word & 0x00FF); + +	rsi_dbg(INIT_ZONE, +		"%s: MASTER_ACCESS_MSBYTE:0x%x\n", __func__, byte); + +	status = rsi_sdio_write_register(adapter, +					 function, +					 SDIO_MASTER_ACCESS_MSBYTE, +					 &byte); +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: fail to access MASTER_ACCESS_MSBYTE\n", +			__func__); +		return -1; +	} + +	byte = (u8)(ms_word >> 8); + +	rsi_dbg(INIT_ZONE, "%s:MASTER_ACCESS_LSBYTE:0x%x\n", __func__, byte); +	status = rsi_sdio_write_register(adapter, +					 function, +					 SDIO_MASTER_ACCESS_LSBYTE, +					 &byte); +	return status; +} + +/** + * rsi_copy_to_card() - This function includes the actual funtionality of + *			copying the TA firmware to the card.Basically this + *			function includes opening the TA file,reading the + *			TA file and writing their values in blocks of data. + * @common: Pointer to the driver private structure. + * @fw: Pointer to the firmware value to be written. + * @len: length of firmware file. + * @num_blocks: Number of blocks to be written to the card. + * + * Return: 0 on success and -1 on failure. + */ +static int rsi_copy_to_card(struct rsi_common *common, +			    const u8 *fw, +			    u32 len, +			    u32 num_blocks) +{ +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u32 indx, ii; +	u32 block_size = dev->tx_blk_size; +	u32 lsb_address; +	__le32 data[] = { TA_HOLD_THREAD_VALUE, TA_SOFT_RST_CLR, +			  TA_PC_ZERO, TA_RELEASE_THREAD_VALUE }; +	u32 address[] = { TA_HOLD_THREAD_REG, TA_SOFT_RESET_REG, +			  TA_TH0_PC_REG, TA_RELEASE_THREAD_REG }; +	u32 base_address; +	u16 msb_address; + +	base_address = TA_LOAD_ADDRESS; +	msb_address = base_address >> 16; + +	for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) { +		lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER); +		if (rsi_sdio_write_register_multiple(adapter, +						     lsb_address, +						     (u8 *)(fw + indx), +						     block_size)) { +			rsi_dbg(ERR_ZONE, +				"%s: Unable to load %s blk\n", __func__, +				FIRMWARE_RSI9113); +			return -1; +		} +		rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii); +		base_address += block_size; +		if ((base_address >> 16) != msb_address) { +			msb_address += 1; +			if (rsi_sdio_master_access_msword(adapter, +							  msb_address)) { +				rsi_dbg(ERR_ZONE, +					"%s: Unable to set ms word reg\n", +					__func__); +				return -1; +			} +		} +	} + +	if (len % block_size) { +		lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER); +		if (rsi_sdio_write_register_multiple(adapter, +						     lsb_address, +						     (u8 *)(fw + indx), +						     len % block_size)) { +			rsi_dbg(ERR_ZONE, +				"%s: Unable to load f/w\n", __func__); +			return -1; +		} +	} +	rsi_dbg(INIT_ZONE, +		"%s: Succesfully loaded TA instructions\n", __func__); + +	if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) { +		rsi_dbg(ERR_ZONE, +			"%s: Unable to set ms word to common reg\n", +			__func__); +		return -1; +	} + +	for (ii = 0; ii < ARRAY_SIZE(data); ii++) { +		/* Bringing TA out of reset */ +		if (rsi_sdio_write_register_multiple(adapter, +						     (address[ii] | +						     RSI_SD_REQUEST_MASTER), +						     (u8 *)&data[ii], +						     4)) { +			rsi_dbg(ERR_ZONE, +				"%s: Unable to hold TA threads\n", __func__); +			return -1; +		} +	} + +	rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__); +	return 0; +} + +/** + * rsi_load_ta_instructions() - This function includes the actual funtionality + *				of loading the TA firmware.This function also + *				includes opening the TA file,reading the TA + *				file and writing their value in blocks of data. + * @common: Pointer to the driver private structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_load_ta_instructions(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u32 len; +	u32 num_blocks; +	const u8 *fw; +	const struct firmware *fw_entry = NULL; +	u32 block_size = dev->tx_blk_size; +	int status = 0; +	u32 base_address; +	u16 msb_address; + +	if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) { +		rsi_dbg(ERR_ZONE, +			"%s: Unable to set ms word to common reg\n", +			__func__); +		return -1; +	} +	base_address = TA_LOAD_ADDRESS; +	msb_address = (base_address >> 16); + +	if (rsi_sdio_master_access_msword(adapter, msb_address)) { +		rsi_dbg(ERR_ZONE, +			"%s: Unable to set ms word reg\n", __func__); +		return -1; +	} + +	status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device); +	if (status < 0) { +		rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n", +			__func__, FIRMWARE_RSI9113); +		return status; +	} + +	fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); +	len = fw_entry->size; + +	if (len % 4) +		len += (4 - (len % 4)); + +	num_blocks = (len / block_size); + +	rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); +	rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); + +	status = rsi_copy_to_card(common, fw, len, num_blocks); +	release_firmware(fw_entry); +	return status; +} + +/** + * rsi_process_pkt() - This Function reads rx_blocks register and figures out + *		       the size of the rx pkt. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_process_pkt(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	u8 num_blks = 0; +	u32 rcv_pkt_len = 0; +	int status = 0; + +	status = rsi_sdio_read_register(adapter, +					SDIO_RX_NUM_BLOCKS_REG, +					&num_blks); + +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: Failed to read pkt length from the card:\n", +			__func__); +		return status; +	} +	rcv_pkt_len = (num_blks * 256); + +	common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL); +	if (!common->rx_data_pkt) { +		rsi_dbg(ERR_ZONE, "%s: Failed in memory allocation\n", +			__func__); +		return -ENOMEM; +	} + +	status = rsi_sdio_host_intf_read_pkt(adapter, +					     common->rx_data_pkt, +					     rcv_pkt_len); +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n", +			__func__); +		goto fail; +	} + +	status = rsi_read_pkt(common, rcv_pkt_len); + +fail: +	kfree(common->rx_data_pkt); +	return status; +} + +/** + * rsi_init_sdio_slave_regs() - This function does the actual initialization + *				of SDBUS slave registers. + * @adapter: Pointer to the adapter structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_init_sdio_slave_regs(struct rsi_hw *adapter) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u8 function = 0; +	u8 byte; +	int status = 0; + +	if (dev->next_read_delay) { +		byte = dev->next_read_delay; +		status = rsi_sdio_write_register(adapter, +						 function, +						 SDIO_NXT_RD_DELAY2, +						 &byte); +		if (status) { +			rsi_dbg(ERR_ZONE, +				"%s: Failed to write SDIO_NXT_RD_DELAY2\n", +				__func__); +			return -1; +		} +	} + +	if (dev->sdio_high_speed_enable) { +		rsi_dbg(INIT_ZONE, "%s: Enabling SDIO High speed\n", __func__); +		byte = 0x3; + +		status = rsi_sdio_write_register(adapter, +						 function, +						 SDIO_REG_HIGH_SPEED, +						 &byte); +		if (status) { +			rsi_dbg(ERR_ZONE, +				"%s: Failed to enable SDIO high speed\n", +				__func__); +			return -1; +		} +	} + +	/* This tells SDIO FIFO when to start read to host */ +	rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__); +	byte = 0x24; + +	status = rsi_sdio_write_register(adapter, +					 function, +					 SDIO_READ_START_LVL, +					 &byte); +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: Failed to write SDIO_READ_START_LVL\n", __func__); +		return -1; +	} + +	rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__); +	byte = (128 - 32); + +	status = rsi_sdio_write_register(adapter, +					 function, +					 SDIO_READ_FIFO_CTL, +					 &byte); +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: Failed to write SDIO_READ_FIFO_CTL\n", __func__); +		return -1; +	} + +	byte = 32; +	status = rsi_sdio_write_register(adapter, +					 function, +					 SDIO_WRITE_FIFO_CTL, +					 &byte); +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: Failed to write SDIO_WRITE_FIFO_CTL\n", __func__); +		return -1; +	} + +	return 0; +} + +/** + * rsi_interrupt_handler() - This function read and process SDIO interrupts. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_interrupt_handler(struct rsi_hw *adapter) +{ +	struct rsi_common *common = adapter->priv; +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	int status; +	enum sdio_interrupt_type isr_type; +	u8 isr_status = 0; +	u8 fw_status = 0; + +	dev->rx_info.sdio_int_counter++; + +	do { +		mutex_lock(&common->tx_rxlock); +		status = rsi_sdio_read_register(common->priv, +						RSI_FN1_INT_REGISTER, +						&isr_status); +		if (status) { +			rsi_dbg(ERR_ZONE, +				"%s: Failed to Read Intr Status Register\n", +				__func__); +			mutex_unlock(&common->tx_rxlock); +			return; +		} + +		if (isr_status == 0) { +			rsi_set_event(&common->tx_thread.event); +			dev->rx_info.sdio_intr_status_zero++; +			mutex_unlock(&common->tx_rxlock); +			return; +		} + +		rsi_dbg(ISR_ZONE, "%s: Intr_status = %x %d %d\n", +			__func__, isr_status, (1 << MSDU_PKT_PENDING), +			(1 << FW_ASSERT_IND)); + +		do { +			RSI_GET_SDIO_INTERRUPT_TYPE(isr_status, isr_type); + +			switch (isr_type) { +			case BUFFER_AVAILABLE: +				dev->rx_info.watch_bufferfull_count = 0; +				dev->rx_info.buffer_full = false; +				dev->rx_info.mgmt_buffer_full = false; +				rsi_sdio_ack_intr(common->priv, +						  (1 << PKT_BUFF_AVAILABLE)); +				rsi_set_event((&common->tx_thread.event)); +				rsi_dbg(ISR_ZONE, +					"%s: ==> BUFFER_AVILABLE <==\n", +					__func__); +				dev->rx_info.buf_avilable_counter++; +				break; + +			case FIRMWARE_ASSERT_IND: +				rsi_dbg(ERR_ZONE, +					"%s: ==> FIRMWARE Assert <==\n", +					__func__); +				status = rsi_sdio_read_register(common->priv, +							SDIO_FW_STATUS_REG, +							&fw_status); +				if (status) { +					rsi_dbg(ERR_ZONE, +						"%s: Failed to read f/w reg\n", +						__func__); +				} else { +					rsi_dbg(ERR_ZONE, +						"%s: Firmware Status is 0x%x\n", +						__func__ , fw_status); +					rsi_sdio_ack_intr(common->priv, +							  (1 << FW_ASSERT_IND)); +				} + +				common->fsm_state = FSM_CARD_NOT_READY; +				break; + +			case MSDU_PACKET_PENDING: +				rsi_dbg(ISR_ZONE, "Pkt pending interrupt\n"); +				dev->rx_info.total_sdio_msdu_pending_intr++; + +				status = rsi_process_pkt(common); +				if (status) { +					rsi_dbg(ERR_ZONE, +						"%s: Failed to read pkt\n", +						__func__); +					mutex_unlock(&common->tx_rxlock); +					return; +				} +				break; +			default: +				rsi_sdio_ack_intr(common->priv, isr_status); +				dev->rx_info.total_sdio_unknown_intr++; +				isr_status = 0; +				rsi_dbg(ISR_ZONE, +					"Unknown Interrupt %x\n", +					isr_status); +				break; +			} +			isr_status ^= BIT(isr_type - 1); +		} while (isr_status); +		mutex_unlock(&common->tx_rxlock); +	} while (1); +} + +/** + * rsi_device_init() - This Function Initializes The HAL. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_device_init(struct rsi_common *common) +{ +	if (rsi_load_ta_instructions(common)) +		return -1; + +	if (rsi_sdio_master_access_msword(common->priv, MISC_CFG_BASE_ADDR)) { +		rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", +			__func__); +		return -1; +	} +	rsi_dbg(INIT_ZONE, +		"%s: Setting ms word to 0x41050000\n", __func__); + +	return 0; +} + +/** + * rsi_sdio_read_buffer_status_register() - This function is used to the read + *					    buffer status register and set + *					    relevant fields in + *					    rsi_91x_sdiodev struct. + * @adapter: Pointer to the driver hw structure. + * @q_num: The Q number whose status is to be found. + * + * Return: status: -1 on failure or else queue full/stop is indicated. + */ +int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num) +{ +	struct rsi_common *common = adapter->priv; +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; +	u8 buf_status = 0; +	int status = 0; + +	status = rsi_sdio_read_register(common->priv, +					RSI_DEVICE_BUFFER_STATUS_REGISTER, +					&buf_status); + +	if (status) { +		rsi_dbg(ERR_ZONE, +			"%s: Failed to read status register\n", __func__); +		return -1; +	} + +	if (buf_status & (BIT(PKT_MGMT_BUFF_FULL))) { +		if (!dev->rx_info.mgmt_buffer_full) +			dev->rx_info.mgmt_buf_full_counter++; +		dev->rx_info.mgmt_buffer_full = true; +	} else { +		dev->rx_info.mgmt_buffer_full = false; +	} + +	if (buf_status & (BIT(PKT_BUFF_FULL))) { +		if (!dev->rx_info.buffer_full) +			dev->rx_info.buf_full_counter++; +		dev->rx_info.buffer_full = true; +	} else { +		dev->rx_info.buffer_full = false; +	} + +	if (buf_status & (BIT(PKT_BUFF_SEMI_FULL))) { +		if (!dev->rx_info.semi_buffer_full) +			dev->rx_info.buf_semi_full_counter++; +		dev->rx_info.semi_buffer_full = true; +	} else { +		dev->rx_info.semi_buffer_full = false; +	} + +	if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full)) +		return QUEUE_FULL; + +	if (dev->rx_info.buffer_full) +		return QUEUE_FULL; + +	return QUEUE_NOT_FULL; +} + +/** + * rsi_sdio_determine_event_timeout() - This Function determines the event + *					timeout duration. + * @adapter: Pointer to the adapter structure. + * + * Return: timeout duration is returned. + */ +int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter) +{ +	struct rsi_91x_sdiodev *dev = +		(struct rsi_91x_sdiodev *)adapter->rsi_dev; + +	/* Once buffer full is seen, event timeout to occur every 2 msecs */ +	if (dev->rx_info.buffer_full) +		return 2; + +	return EVENT_WAIT_FOREVER; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c new file mode 100644 index 00000000000..4c46e5631e2 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -0,0 +1,587 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + * + */ + +#include <linux/module.h> +#include "rsi_usb.h" + +/** + * rsi_usb_card_write() - This function writes to the USB Card. + * @adapter: Pointer to the adapter structure. + * @buf: Pointer to the buffer from where the data has to be taken. + * @len: Length to be written. + * @endpoint: Type of endpoint. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_card_write(struct rsi_hw *adapter, +			      void *buf, +			      u16 len, +			      u8 endpoint) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	int status; +	s32 transfer; + +	status = usb_bulk_msg(dev->usbdev, +			      usb_sndbulkpipe(dev->usbdev, +			      dev->bulkout_endpoint_addr[endpoint - 1]), +			      buf, +			      len, +			      &transfer, +			      HZ * 5); + +	if (status < 0) { +		rsi_dbg(ERR_ZONE, +			"Card write failed with error code :%10d\n", status); +		dev->write_fail = 1; +	} +	return status; +} + +/** + * rsi_write_multiple() - This function writes multiple bytes of information + *			  to the USB card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_write_multiple(struct rsi_hw *adapter, +			      u8 endpoint, +			      u8 *data, +			      u32 count) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	u8 *seg = dev->tx_buffer; + +	if (dev->write_fail) +		return 0; + +	if (endpoint == MGMT_EP) { +		memset(seg, 0, RSI_USB_TX_HEAD_ROOM); +		memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count); +	} else { +		seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM); +	} + +	return rsi_usb_card_write(adapter, +				  seg, +				  count + RSI_USB_TX_HEAD_ROOM, +				  endpoint); +} + +/** + * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk + *					  endpoints to the device. + * @interface: Pointer to the USB interface structure. + * @adapter: Pointer to the adapter structure. + * + * Return: ret_val: 0 on success, -ENOMEM on failure. + */ +static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, +					      struct rsi_hw *adapter) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	struct usb_host_interface *iface_desc; +	struct usb_endpoint_descriptor *endpoint; +	__le16 buffer_size; +	int ii, bep_found = 0; + +	iface_desc = &(interface->altsetting[0]); + +	for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { +		endpoint = &(iface_desc->endpoint[ii].desc); + +		if ((!(dev->bulkin_endpoint_addr)) && +		    (endpoint->bEndpointAddress & USB_DIR_IN) && +		    ((endpoint->bmAttributes & +		    USB_ENDPOINT_XFERTYPE_MASK) == +		    USB_ENDPOINT_XFER_BULK)) { +			buffer_size = endpoint->wMaxPacketSize; +			dev->bulkin_size = buffer_size; +			dev->bulkin_endpoint_addr = +				endpoint->bEndpointAddress; +		} + +		if (!dev->bulkout_endpoint_addr[bep_found] && +		    !(endpoint->bEndpointAddress & USB_DIR_IN) && +		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == +		      USB_ENDPOINT_XFER_BULK)) { +			dev->bulkout_endpoint_addr[bep_found] = +				endpoint->bEndpointAddress; +			buffer_size = endpoint->wMaxPacketSize; +			dev->bulkout_size[bep_found] = buffer_size; +			bep_found++; +		} + +		if (bep_found >= MAX_BULK_EP) +			break; +	} + +	if (!(dev->bulkin_endpoint_addr) && +	    (dev->bulkout_endpoint_addr[0])) +		return -EINVAL; + +	return 0; +} + +/* rsi_usb_reg_read() - This function reads data from given register address. + * @usbdev: Pointer to the usb_device structure. + * @reg: Address of the register to be read. + * @value: Value to be read. + * @len: length of data to be read. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_reg_read(struct usb_device *usbdev, +			    u32 reg, +			    u16 *value, +			    u16 len) +{ +	u8 *buf; +	int status = -ENOMEM; + +	buf  = kmalloc(0x04, GFP_KERNEL); +	if (!buf) +		return status; + +	status = usb_control_msg(usbdev, +				 usb_rcvctrlpipe(usbdev, 0), +				 USB_VENDOR_REGISTER_READ, +				 USB_TYPE_VENDOR, +				 ((reg & 0xffff0000) >> 16), (reg & 0xffff), +				 (void *)buf, +				 len, +				 HZ * 5); + +	*value = (buf[0] | (buf[1] << 8)); +	if (status < 0) { +		rsi_dbg(ERR_ZONE, +			"%s: Reg read failed with error code :%d\n", +			__func__, status); +	} +	kfree(buf); + +	return status; +} + +/** + * rsi_usb_reg_write() - This function writes the given data into the given + *			 register address. + * @usbdev: Pointer to the usb_device structure. + * @reg: Address of the register. + * @value: Value to write. + * @len: Length of data to be written. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_reg_write(struct usb_device *usbdev, +			     u32 reg, +			     u16 value, +			     u16 len) +{ +	u8 *usb_reg_buf; +	int status = -ENOMEM; + +	usb_reg_buf  = kmalloc(0x04, GFP_KERNEL); +	if (!usb_reg_buf) +		return status; + +	usb_reg_buf[0] = (value & 0x00ff); +	usb_reg_buf[1] = (value & 0xff00) >> 8; +	usb_reg_buf[2] = 0x0; +	usb_reg_buf[3] = 0x0; + +	status = usb_control_msg(usbdev, +				 usb_sndctrlpipe(usbdev, 0), +				 USB_VENDOR_REGISTER_WRITE, +				 USB_TYPE_VENDOR, +				 ((reg & 0xffff0000) >> 16), +				 (reg & 0xffff), +				 (void *)usb_reg_buf, +				 len, +				 HZ * 5); +	if (status < 0) { +		rsi_dbg(ERR_ZONE, +			"%s: Reg write failed with error code :%d\n", +			__func__, status); +	} +	kfree(usb_reg_buf); + +	return status; +} + +/** + * rsi_rx_done_handler() - This function is called when a packet is received + *			   from USB stack. This is callback to recieve done. + * @urb: Received URB. + * + * Return: None. + */ +static void rsi_rx_done_handler(struct urb *urb) +{ +	struct rsi_hw *adapter = urb->context; +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + +	if (urb->status) +		return; + +	rsi_set_event(&dev->rx_thread.event); +} + +/** + * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. + * @adapter: Pointer to the adapter structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_rx_urb_submit(struct rsi_hw *adapter) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	struct urb *urb = dev->rx_usb_urb[0]; +	int status; + +	usb_fill_bulk_urb(urb, +			  dev->usbdev, +			  usb_rcvbulkpipe(dev->usbdev, +				dev->bulkin_endpoint_addr), +			  urb->transfer_buffer, +			  3000, +			  rsi_rx_done_handler, +			  adapter); + +	status = usb_submit_urb(urb, GFP_KERNEL); +	if (status) +		rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); + +	return status; +} + +/** + * rsi_usb_write_register_multiple() - This function writes multiple bytes of + *				       information to multiple registers. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written on to the registers. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_usb_write_register_multiple(struct rsi_hw *adapter, +				    u32 addr, +				    u8 *data, +				    u32 count) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	u8 *buf; +	u8 transfer; +	int status = 0; + +	buf = kzalloc(4096, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	while (count) { +		transfer = (u8)(min_t(u32, count, 4096)); +		memcpy(buf, data, transfer); +		status = usb_control_msg(dev->usbdev, +					 usb_sndctrlpipe(dev->usbdev, 0), +					 USB_VENDOR_REGISTER_WRITE, +					 USB_TYPE_VENDOR, +					 ((addr & 0xffff0000) >> 16), +					 (addr & 0xffff), +					 (void *)buf, +					 transfer, +					 HZ * 5); +		if (status < 0) { +			rsi_dbg(ERR_ZONE, +				"Reg write failed with error code :%d\n", +				status); +		} else { +			count -= transfer; +			data += transfer; +			addr += transfer; +		} +	} + +	kfree(buf); +	return 0; +} + +/** + *rsi_usb_host_intf_write_pkt() - This function writes the packet to the + *				   USB card. + * @adapter: Pointer to the adapter structure. + * @pkt: Pointer to the data to be written on to the card. + * @len: Length of the data to be written on to the card. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, +				       u8 *pkt, +				       u32 len) +{ +	u32 queueno = ((pkt[1] >> 4) & 0xf); +	u8 endpoint; + +	endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP); + +	return rsi_write_multiple(adapter, +				  endpoint, +				  (u8 *)pkt, +				  len); +} + +/** + * rsi_deinit_usb_interface() - This function deinitializes the usb interface. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +static void rsi_deinit_usb_interface(struct rsi_hw *adapter) +{ +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + +	rsi_kill_thread(&dev->rx_thread); +	kfree(adapter->priv->rx_data_pkt); +	kfree(dev->tx_buffer); +} + +/** + * rsi_init_usb_interface() - This function initializes the usb interface. + * @adapter: Pointer to the adapter structure. + * @pfunction: Pointer to USB interface structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_init_usb_interface(struct rsi_hw *adapter, +				  struct usb_interface *pfunction) +{ +	struct rsi_91x_usbdev *rsi_dev; +	struct rsi_common *common = adapter->priv; +	int status; + +	rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL); +	if (!rsi_dev) +		return -ENOMEM; + +	adapter->rsi_dev = rsi_dev; +	rsi_dev->usbdev = interface_to_usbdev(pfunction); + +	if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) +		return -EINVAL; + +	adapter->device = &pfunction->dev; +	usb_set_intfdata(pfunction, adapter); + +	common->rx_data_pkt = kmalloc(2048, GFP_KERNEL); +	if (!common->rx_data_pkt) { +		rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n", +			__func__); +		return -ENOMEM; +	} + +	rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC); +	rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL); +	rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt; +	rsi_dev->tx_blk_size = 252; + +	/* Initializing function callbacks */ +	adapter->rx_urb_submit = rsi_rx_urb_submit; +	adapter->host_intf_write_pkt = rsi_usb_host_intf_write_pkt; +	adapter->check_hw_queue_status = rsi_usb_check_queue_status; +	adapter->determine_event_timeout = rsi_usb_event_timeout; + +	rsi_init_event(&rsi_dev->rx_thread.event); +	status = rsi_create_kthread(common, &rsi_dev->rx_thread, +				    rsi_usb_rx_thread, "RX-Thread"); +	if (status) { +		rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); +		goto fail; +	} + +#ifdef CONFIG_RSI_DEBUGFS +	/* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */ +	adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1); +#endif + +	rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); +	return 0; + +fail: +	kfree(rsi_dev->tx_buffer); +	kfree(common->rx_data_pkt); +	return status; +} + +/** + * rsi_probe() - This function is called by kernel when the driver provided + *		 Vendor and device IDs are matched. All the initialization + *		 work is done here. + * @pfunction: Pointer to the USB interface structure. + * @id: Pointer to the usb_device_id structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_probe(struct usb_interface *pfunction, +		     const struct usb_device_id *id) +{ +	struct rsi_hw *adapter; +	struct rsi_91x_usbdev *dev; +	u16 fw_status; + +	rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); + +	adapter = rsi_91x_init(); +	if (!adapter) { +		rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", +			__func__); +		return 1; +	} + +	if (rsi_init_usb_interface(adapter, pfunction)) { +		rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", +			__func__); +		goto err; +	} + +	rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__); + +	dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + +	if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0) +		goto err1; +	else +		fw_status &= 1; + +	if (!fw_status) { +		if (rsi_usb_device_init(adapter->priv)) { +			rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", +				__func__); +			goto err1; +		} + +		if (rsi_usb_reg_write(dev->usbdev, +				      USB_INTERNAL_REG_1, +				      RSI_USB_READY_MAGIC_NUM, 1) < 0) +			goto err1; +		rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__); +	} + +	if (rsi_rx_urb_submit(adapter)) +		goto err1; + +	return 0; +err1: +	rsi_deinit_usb_interface(adapter); +err: +	rsi_91x_deinit(adapter); +	rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); +	return 1; +} + +/** + * rsi_disconnect() - This function performs the reverse of the probe function, + *		      it deintialize the driver structure. + * @pfunction: Pointer to the USB interface structure. + * + * Return: None. + */ +static void rsi_disconnect(struct usb_interface *pfunction) +{ +	struct rsi_hw *adapter = usb_get_intfdata(pfunction); + +	if (!adapter) +		return; + +	rsi_mac80211_detach(adapter); +	rsi_deinit_usb_interface(adapter); +	rsi_91x_deinit(adapter); + +	rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__); +} + +#ifdef CONFIG_PM +static int rsi_suspend(struct usb_interface *intf, pm_message_t message) +{ +	/* Not yet implemented */ +	return -ENOSYS; +} + +static int rsi_resume(struct usb_interface *intf) +{ +	/* Not yet implemented */ +	return -ENOSYS; +} +#endif + +static const struct usb_device_id rsi_dev_table[] = { +	{ USB_DEVICE(0x0303, 0x0100) }, +	{ USB_DEVICE(0x041B, 0x0301) }, +	{ USB_DEVICE(0x041B, 0x0201) }, +	{ USB_DEVICE(0x041B, 0x9330) }, +	{ /* Blank */}, +}; + +static struct usb_driver rsi_driver = { +	.name       = "RSI-USB WLAN", +	.probe      = rsi_probe, +	.disconnect = rsi_disconnect, +	.id_table   = rsi_dev_table, +#ifdef CONFIG_PM +	.suspend    = rsi_suspend, +	.resume     = rsi_resume, +#endif +}; + +/** + * rsi_module_init() - This function registers the client driver. + * @void: Void. + * + * Return: 0 on success. + */ +static int rsi_module_init(void) +{ +	usb_register(&rsi_driver); +	rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); +	return 0; +} + +/** + * rsi_module_exit() - This function unregisters the client driver. + * @void: Void. + * + * Return: None. + */ +static void rsi_module_exit(void) +{ +	usb_deregister(&rsi_driver); +	rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__); +} + +module_init(rsi_module_init); +module_exit(rsi_module_exit); + +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Common USB layer for RSI drivers"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_DEVICE_TABLE(usb, rsi_dev_table); +MODULE_FIRMWARE(FIRMWARE_RSI9113); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c new file mode 100644 index 00000000000..1106ce76707 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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. + * + */ + +#include <linux/firmware.h> +#include "rsi_usb.h" + +/** + * rsi_copy_to_card() - This function includes the actual funtionality of + *			copying the TA firmware to the card.Basically this + *			function includes opening the TA file,reading the TA + *			file and writing their values in blocks of data. + * @common: Pointer to the driver private structure. + * @fw: Pointer to the firmware value to be written. + * @len: length of firmware file. + * @num_blocks: Number of blocks to be written to the card. + * + * Return: 0 on success and -1 on failure. + */ +static int rsi_copy_to_card(struct rsi_common *common, +			    const u8 *fw, +			    u32 len, +			    u32 num_blocks) +{ +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	u32 indx, ii; +	u32 block_size = dev->tx_blk_size; +	u32 lsb_address; +	u32 base_address; + +	base_address = TA_LOAD_ADDRESS; + +	for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) { +		lsb_address = base_address; +		if (rsi_usb_write_register_multiple(adapter, +						    lsb_address, +						    (u8 *)(fw + indx), +						    block_size)) { +			rsi_dbg(ERR_ZONE, +				"%s: Unable to load %s blk\n", __func__, +				FIRMWARE_RSI9113); +			return -EIO; +		} +		rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii); +		base_address += block_size; +	} + +	if (len % block_size) { +		lsb_address = base_address; +		if (rsi_usb_write_register_multiple(adapter, +						    lsb_address, +						    (u8 *)(fw + indx), +						    len % block_size)) { +			rsi_dbg(ERR_ZONE, +				"%s: Unable to load %s blk\n", __func__, +				FIRMWARE_RSI9113); +			return -EIO; +		} +	} +	rsi_dbg(INIT_ZONE, +		"%s: Succesfully loaded %s instructions\n", __func__, +		FIRMWARE_RSI9113); + +	rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__); +	return 0; +} + +/** + * rsi_usb_rx_thread() - This is a kernel thread to receive the packets from + *			 the USB device. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +void rsi_usb_rx_thread(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	int status; + +	do { +		rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER); + +		if (atomic_read(&dev->rx_thread.thread_done)) +			goto out; + +		mutex_lock(&common->tx_rxlock); +		status = rsi_read_pkt(common, 0); +		if (status) { +			rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__); +			mutex_unlock(&common->tx_rxlock); +			return; +		} +		mutex_unlock(&common->tx_rxlock); +		rsi_reset_event(&dev->rx_thread.event); +		if (adapter->rx_urb_submit(adapter)) { +			rsi_dbg(ERR_ZONE, +				"%s: Failed in urb submission", __func__); +			return; +		} +	} while (1); + +out: +	rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__); +	complete_and_exit(&dev->rx_thread.completion, 0); +} + + +/** + * rsi_load_ta_instructions() - This function includes the actual funtionality + *				of loading the TA firmware.This function also + *				includes opening the TA file,reading the TA + *				file and writing their value in blocks of data. + * @common: Pointer to the driver private structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_load_ta_instructions(struct rsi_common *common) +{ +	struct rsi_hw *adapter = common->priv; +	struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; +	const struct firmware *fw_entry = NULL; +	u32 block_size = dev->tx_blk_size; +	const u8 *fw; +	u32 num_blocks, len; +	int status = 0; + +	status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device); +	if (status < 0) { +		rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n", +			__func__, FIRMWARE_RSI9113); +		return status; +	} + +	fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); +	len = fw_entry->size; + +	if (len % 4) +		len += (4 - (len % 4)); + +	num_blocks = (len / block_size); + +	rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); +	rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); + +	status = rsi_copy_to_card(common, fw, len, num_blocks); +	release_firmware(fw_entry); +	return status; +} + +/** + * rsi_device_init() - This Function Initializes The HAL. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_usb_device_init(struct rsi_common *common) +{ +	if (rsi_load_ta_instructions(common)) +		return -EIO; + +	return 0; +		} diff --git a/drivers/net/wireless/rsi/rsi_boot_params.h b/drivers/net/wireless/rsi/rsi_boot_params.h new file mode 100644 index 00000000000..5e2721f7909 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_boot_params.h @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_BOOTPARAMS_HEADER_H__ +#define __RSI_BOOTPARAMS_HEADER_H__ + +#define CRYSTAL_GOOD_TIME                BIT(0) +#define BOOTUP_MODE_INFO                 BIT(1) +#define WIFI_TAPLL_CONFIGS               BIT(5) +#define WIFI_PLL960_CONFIGS              BIT(6) +#define WIFI_AFEPLL_CONFIGS              BIT(7) +#define WIFI_SWITCH_CLK_CONFIGS          BIT(8) + +#define TA_PLL_M_VAL_20                  8 +#define TA_PLL_N_VAL_20                  1 +#define TA_PLL_P_VAL_20                  4 + +#define PLL960_M_VAL_20                  0x14 +#define PLL960_N_VAL_20                  0 +#define PLL960_P_VAL_20                  5 + +#define UMAC_CLK_40MHZ                   40 + +#define TA_PLL_M_VAL_40                  46 +#define TA_PLL_N_VAL_40                  3 +#define TA_PLL_P_VAL_40                  3 + +#define PLL960_M_VAL_40                  0x14 +#define PLL960_N_VAL_40                  0 +#define PLL960_P_VAL_40                  5 + +#define UMAC_CLK_20BW \ +	(((TA_PLL_M_VAL_20 + 1) * 40) / \ +	 ((TA_PLL_N_VAL_20 + 1) * (TA_PLL_P_VAL_20 + 1))) +#define VALID_20 \ +	(WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS) +#define UMAC_CLK_40BW   \ +	(((TA_PLL_M_VAL_40 + 1) * 40) / \ +	 ((TA_PLL_N_VAL_40 + 1) * (TA_PLL_P_VAL_40 + 1))) +#define VALID_40 \ +	(WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS | \ +	 WIFI_TAPLL_CONFIGS | CRYSTAL_GOOD_TIME | BOOTUP_MODE_INFO) + +/* structure to store configs related to TAPLL programming */ +struct tapll_info { +	__le16 pll_reg_1; +	__le16 pll_reg_2; +} __packed; + +/* structure to store configs related to PLL960 programming */ +struct pll960_info { +	__le16 pll_reg_1; +	__le16 pll_reg_2; +	__le16 pll_reg_3; +} __packed; + +/* structure to store configs related to AFEPLL programming */ +struct afepll_info { +	__le16 pll_reg; +} __packed; + +/* structure to store configs related to pll configs */ +struct pll_config { +	struct tapll_info tapll_info_g; +	struct pll960_info pll960_info_g; +	struct afepll_info afepll_info_g; +} __packed; + +/* structure to store configs related to UMAC clk programming */ +struct switch_clk { +	__le16 switch_clk_info; +	/* If switch_bbp_lmac_clk_reg is set then this value will be programmed +	 * into reg +	 */ +	__le16 bbp_lmac_clk_reg_val; +	/* if switch_umac_clk is set then this value will be programmed */ +	__le16 umac_clock_reg_config; +	/* if switch_qspi_clk is set then this value will be programmed */ +	__le16 qspi_uart_clock_reg_config; +} __packed; + +struct device_clk_info { +	struct pll_config pll_config_g; +	struct switch_clk switch_clk_g; +} __packed; + +struct bootup_params { +	__le16 magic_number; +	__le16 crystal_good_time; +	__le32 valid; +	__le32 reserved_for_valids; +	__le16 bootup_mode_info; +	/* configuration used for digital loop back */ +	__le16 digital_loop_back_params; +	__le16 rtls_timestamp_en; +	__le16 host_spi_intr_cfg; +	struct device_clk_info device_clk_info[3]; +	/* ulp buckboost wait time  */ +	__le32 buckboost_wakeup_cnt; +	/* pmu wakeup wait time & WDT EN info */ +	__le16 pmu_wakeup_wait; +	u8 shutdown_wait_time; +	/* Sleep clock source selection */ +	u8 pmu_slp_clkout_sel; +	/* WDT programming values */ +	__le32 wdt_prog_value; +	/* WDT soc reset delay */ +	__le32 wdt_soc_rst_delay; +	/* dcdc modes configs */ +	__le32 dcdc_operation_mode; +	__le32 soc_reset_wait_cnt; +} __packed; +#endif diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h new file mode 100644 index 00000000000..d3fbe33d232 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_COMMON_H__ +#define __RSI_COMMON_H__ + +#include <linux/kthread.h> + +#define EVENT_WAIT_FOREVER              0 +#define TA_LOAD_ADDRESS                 0x00 +#define FIRMWARE_RSI9113                "rsi_91x.fw" +#define QUEUE_NOT_FULL                  1 +#define QUEUE_FULL                      0 + +static inline int rsi_init_event(struct rsi_event *pevent) +{ +	atomic_set(&pevent->event_condition, 1); +	init_waitqueue_head(&pevent->event_queue); +	return 0; +} + +static inline int rsi_wait_event(struct rsi_event *event, u32 timeout) +{ +	int status = 0; + +	if (!timeout) +		status = wait_event_interruptible(event->event_queue, +				(atomic_read(&event->event_condition) == 0)); +	else +		status = wait_event_interruptible_timeout(event->event_queue, +				(atomic_read(&event->event_condition) == 0), +				timeout); +	return status; +} + +static inline void rsi_set_event(struct rsi_event *event) +{ +	atomic_set(&event->event_condition, 0); +	wake_up_interruptible(&event->event_queue); +} + +static inline void rsi_reset_event(struct rsi_event *event) +{ +	atomic_set(&event->event_condition, 1); +} + +static inline int rsi_create_kthread(struct rsi_common *common, +				     struct rsi_thread *thread, +				     void *func_ptr, +				     u8 *name) +{ +	init_completion(&thread->completion); +	thread->task = kthread_run(func_ptr, common, "%s", name); +	if (IS_ERR(thread->task)) +		return (int)PTR_ERR(thread->task); + +	return 0; +} + +static inline int rsi_kill_thread(struct rsi_thread *handle) +{ +	atomic_inc(&handle->thread_done); +	rsi_set_event(&handle->event); + +	wait_for_completion(&handle->completion); +	return kthread_stop(handle->task); +} + +void rsi_mac80211_detach(struct rsi_hw *hw); +u16 rsi_get_connected_channel(struct rsi_hw *adapter); +struct rsi_hw *rsi_91x_init(void); +void rsi_91x_deinit(struct rsi_hw *adapter); +int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len); +#endif diff --git a/drivers/net/wireless/rsi/rsi_debugfs.h b/drivers/net/wireless/rsi/rsi_debugfs.h new file mode 100644 index 00000000000..580ad3b3f71 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_debugfs.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_DEBUGFS_H__ +#define __RSI_DEBUGFS_H__ + +#include "rsi_main.h" +#include <linux/debugfs.h> + +#ifndef CONFIG_RSI_DEBUGFS +static inline int rsi_init_dbgfs(struct rsi_hw *adapter) +{ +	return 0; +} + +static inline void rsi_remove_dbgfs(struct rsi_hw *adapter) +{ +	return; +} +#else +struct rsi_dbg_files { +	const char *name; +	umode_t perms; +	const struct file_operations fops; +}; + +struct rsi_debugfs { +	struct dentry *subdir; +	struct rsi_dbg_ops *dfs_get_ops; +	struct dentry *rsi_files[MAX_DEBUGFS_ENTRIES]; +}; +int rsi_init_dbgfs(struct rsi_hw *adapter); +void rsi_remove_dbgfs(struct rsi_hw *adapter); +#endif +#endif diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h new file mode 100644 index 00000000000..2cb73e7edb9 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_MAIN_H__ +#define __RSI_MAIN_H__ + +#include <linux/string.h> +#include <linux/skbuff.h> +#include <net/mac80211.h> + +#define ERR_ZONE                        BIT(0)  /* For Error Msgs             */ +#define INFO_ZONE                       BIT(1)  /* For General Status Msgs    */ +#define INIT_ZONE                       BIT(2)  /* For Driver Init Seq Msgs   */ +#define MGMT_TX_ZONE                    BIT(3)  /* For TX Mgmt Path Msgs      */ +#define MGMT_RX_ZONE                    BIT(4)  /* For RX Mgmt Path Msgs      */ +#define DATA_TX_ZONE                    BIT(5)  /* For TX Data Path Msgs      */ +#define DATA_RX_ZONE                    BIT(6)  /* For RX Data Path Msgs      */ +#define FSM_ZONE                        BIT(7)  /* For State Machine Msgs     */ +#define ISR_ZONE                        BIT(8)  /* For Interrupt Msgs         */ + +#define FSM_CARD_NOT_READY              0 +#define FSM_BOOT_PARAMS_SENT            1 +#define FSM_EEPROM_READ_MAC_ADDR        2 +#define FSM_RESET_MAC_SENT              3 +#define FSM_RADIO_CAPS_SENT             4 +#define FSM_BB_RF_PROG_SENT             5 +#define FSM_MAC_INIT_DONE               6 + +extern u32 rsi_zone_enabled; +extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); + +#define RSI_MAX_VIFS                    1 +#define NUM_EDCA_QUEUES                 4 +#define IEEE80211_ADDR_LEN              6 +#define FRAME_DESC_SZ                   16 +#define MIN_802_11_HDR_LEN              24 + +#define DATA_QUEUE_WATER_MARK           400 +#define MIN_DATA_QUEUE_WATER_MARK       300 +#define MULTICAST_WATER_MARK            200 +#define MAC_80211_HDR_FRAME_CONTROL     0 +#define WME_NUM_AC                      4 +#define NUM_SOFT_QUEUES                 5 +#define MAX_HW_QUEUES                   8 +#define INVALID_QUEUE                   0xff +#define MAX_CONTINUOUS_VO_PKTS          8 +#define MAX_CONTINUOUS_VI_PKTS          4 + +/* Queue information */ +#define RSI_WIFI_MGMT_Q                 0x4 +#define RSI_WIFI_DATA_Q                 0x5 +#define IEEE80211_MGMT_FRAME            0x00 +#define IEEE80211_CTL_FRAME             0x04 + +#define IEEE80211_QOS_TID               0x0f +#define IEEE80211_NONQOS_TID            16 + +#define MAX_DEBUGFS_ENTRIES             4 + +#define TID_TO_WME_AC(_tid) (      \ +	((_tid) == 0 || (_tid) == 3) ? BE_Q : \ +	((_tid) < 3) ? BK_Q : \ +	((_tid) < 6) ? VI_Q : \ +	VO_Q) + +#define WME_AC(_q) (    \ +	((_q) == BK_Q) ? IEEE80211_AC_BK : \ +	((_q) == BE_Q) ? IEEE80211_AC_BE : \ +	((_q) == VI_Q) ? IEEE80211_AC_VI : \ +	IEEE80211_AC_VO) + +struct version_info { +	u16 major; +	u16 minor; +	u16 release_num; +	u16 patch_num; +} __packed; + +struct skb_info { +	s8 rssi; +	u32 flags; +	u16 channel; +	s8 tid; +	s8 sta_id; +}; + +enum edca_queue { +	BK_Q, +	BE_Q, +	VI_Q, +	VO_Q, +	MGMT_SOFT_Q +}; + +struct security_info { +	bool security_enable; +	u32 ptk_cipher; +	u32 gtk_cipher; +}; + +struct wmm_qinfo { +	s32 weight; +	s32 wme_params; +	s32 pkt_contended; +}; + +struct transmit_q_stats { +	u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 1]; +	u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 1]; +}; + +struct vif_priv { +	bool is_ht; +	bool sgi; +	u16 seq_start; +}; + +struct rsi_event { +	atomic_t event_condition; +	wait_queue_head_t event_queue; +}; + +struct rsi_thread { +	void (*thread_function)(void *); +	struct completion completion; +	struct task_struct *task; +	struct rsi_event event; +	atomic_t thread_done; +}; + +struct rsi_hw; + +struct rsi_common { +	struct rsi_hw *priv; +	struct vif_priv vif_info[RSI_MAX_VIFS]; + +	bool mgmt_q_block; +	struct version_info driver_ver; +	struct version_info fw_ver; + +	struct rsi_thread tx_thread; +	struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1]; +	/* Mutex declaration */ +	struct mutex mutex; +	/* Mutex used between tx/rx threads */ +	struct mutex tx_rxlock; +	u8 endpoint; + +	/* Channel/band related */ +	u8 band; +	u8 channel_width; + +	u16 rts_threshold; +	u16 bitrate_mask[2]; +	u32 fixedrate_mask[2]; + +	u8 rf_reset; +	struct transmit_q_stats tx_stats; +	struct security_info secinfo; +	struct wmm_qinfo tx_qinfo[NUM_EDCA_QUEUES]; +	struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES]; +	u8 mac_addr[IEEE80211_ADDR_LEN]; + +	/* state related */ +	u32 fsm_state; +	bool init_done; +	u8 bb_rf_prog_count; +	bool iface_down; + +	/* Generic */ +	u8 channel; +	u8 *rx_data_pkt; +	u8 mac_id; +	u8 radio_id; +	u16 rate_pwr[20]; +	u16 min_rate; + +	/* WMM algo related */ +	u8 selected_qnum; +	u32 pkt_cnt; +	u8 min_weight; +}; + +struct rsi_hw { +	struct rsi_common *priv; +	struct ieee80211_hw *hw; +	struct ieee80211_vif *vifs[RSI_MAX_VIFS]; +	struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES]; +	struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + +	struct device *device; +	u8 sc_nvifs; + +#ifdef CONFIG_RSI_DEBUGFS +	struct rsi_debugfs *dfsentry; +	u8 num_debugfs_entries; +#endif +	void *rsi_dev; +	int (*host_intf_read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); +	int (*host_intf_write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); +	int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num); +	int (*rx_urb_submit)(struct rsi_hw *adapter); +	int (*determine_event_timeout)(struct rsi_hw *adapter); +}; +#endif diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h new file mode 100644 index 00000000000..225215a3b8b --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -0,0 +1,286 @@ +/** + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_MGMT_H__ +#define __RSI_MGMT_H__ + +#include <linux/sort.h> +#include "rsi_boot_params.h" +#include "rsi_main.h" + +#define MAX_MGMT_PKT_SIZE               512 +#define RSI_NEEDED_HEADROOM             80 +#define RSI_RCV_BUFFER_LEN              2000 + +#define RSI_11B_MODE                    0 +#define RSI_11G_MODE                    BIT(7) +#define RETRY_COUNT                     8 +#define RETRY_LONG                      4 +#define RETRY_SHORT                     7 +#define WMM_SHORT_SLOT_TIME             9 +#define SIFS_DURATION                   16 + +#define KEY_TYPE_CLEAR                  0 +#define RSI_PAIRWISE_KEY                1 +#define RSI_GROUP_KEY                   2 + +/* EPPROM_READ_ADDRESS */ +#define WLAN_MAC_EEPROM_ADDR            40 +#define WLAN_MAC_MAGIC_WORD_LEN         0x01 +#define WLAN_HOST_MODE_LEN              0x04 +#define WLAN_FW_VERSION_LEN             0x08 +#define MAGIC_WORD                      0x5A + +/* Receive Frame Types */ +#define TA_CONFIRM_TYPE                 0x01 +#define RX_DOT11_MGMT                   0x02 +#define TX_STATUS_IND                   0x04 +#define PROBEREQ_CONFIRM                2 +#define CARD_READY_IND                  0x00 + +#define RSI_DELETE_PEER                 0x0 +#define RSI_ADD_PEER                    0x1 +#define START_AMPDU_AGGR                0x1 +#define STOP_AMPDU_AGGR                 0x0 +#define INTERNAL_MGMT_PKT               0x99 + +#define PUT_BBP_RESET                   0 +#define BBP_REG_WRITE                   0 +#define RF_RESET_ENABLE                 BIT(3) +#define RATE_INFO_ENABLE                BIT(0) +#define RSI_BROADCAST_PKT               BIT(9) + +#define UPPER_20_ENABLE                 (0x2 << 12) +#define LOWER_20_ENABLE                 (0x4 << 12) +#define FULL40M_ENABLE                  0x6 + +#define RSI_LMAC_CLOCK_80MHZ            0x1 +#define RSI_ENABLE_40MHZ                (0x1 << 3) + +#define RX_BA_INDICATION                1 +#define RSI_TBL_SZ                      40 +#define MAX_RETRIES                     8 +#define RSI_IFTYPE_STATION		 0 + +#define STD_RATE_MCS7                   0x07 +#define STD_RATE_MCS6                   0x06 +#define STD_RATE_MCS5                   0x05 +#define STD_RATE_MCS4                   0x04 +#define STD_RATE_MCS3                   0x03 +#define STD_RATE_MCS2                   0x02 +#define STD_RATE_MCS1                   0x01 +#define STD_RATE_MCS0                   0x00 +#define STD_RATE_54                     0x6c +#define STD_RATE_48                     0x60 +#define STD_RATE_36                     0x48 +#define STD_RATE_24                     0x30 +#define STD_RATE_18                     0x24 +#define STD_RATE_12                     0x18 +#define STD_RATE_11                     0x16 +#define STD_RATE_09                     0x12 +#define STD_RATE_06                     0x0C +#define STD_RATE_5_5                    0x0B +#define STD_RATE_02                     0x04 +#define STD_RATE_01                     0x02 + +#define RSI_RF_TYPE                     1 +#define RSI_RATE_00                     0x00 +#define RSI_RATE_1                      0x0 +#define RSI_RATE_2                      0x2 +#define RSI_RATE_5_5                    0x4 +#define RSI_RATE_11                     0x6 +#define RSI_RATE_6                      0x8b +#define RSI_RATE_9                      0x8f +#define RSI_RATE_12                     0x8a +#define RSI_RATE_18                     0x8e +#define RSI_RATE_24                     0x89 +#define RSI_RATE_36                     0x8d +#define RSI_RATE_48                     0x88 +#define RSI_RATE_54                     0x8c +#define RSI_RATE_MCS0                   0x100 +#define RSI_RATE_MCS1                   0x101 +#define RSI_RATE_MCS2                   0x102 +#define RSI_RATE_MCS3                   0x103 +#define RSI_RATE_MCS4                   0x104 +#define RSI_RATE_MCS5                   0x105 +#define RSI_RATE_MCS6                   0x106 +#define RSI_RATE_MCS7                   0x107 +#define RSI_RATE_MCS7_SG                0x307 + +#define BW_20MHZ                        0 +#define BW_40MHZ                        1 + +#define RSI_SUPP_FILTERS	(FIF_ALLMULTI | FIF_PROBE_REQ |\ +				 FIF_BCN_PRBRESP_PROMISC) +enum opmode { +	STA_OPMODE = 1, +	AP_OPMODE = 2 +}; + +extern struct ieee80211_rate rsi_rates[12]; +extern const u16 rsi_mcsrates[8]; + +enum sta_notify_events { +	STA_CONNECTED = 0, +	STA_DISCONNECTED, +	STA_TX_ADDBA_DONE, +	STA_TX_DELBA, +	STA_RX_ADDBA_DONE, +	STA_RX_DELBA +}; + +/* Send Frames Types */ +enum cmd_frame_type { +	TX_DOT11_MGMT, +	RESET_MAC_REQ, +	RADIO_CAPABILITIES, +	BB_PROG_VALUES_REQUEST, +	RF_PROG_VALUES_REQUEST, +	WAKEUP_SLEEP_REQUEST, +	SCAN_REQUEST, +	TSF_UPDATE, +	PEER_NOTIFY, +	BLOCK_UNBLOCK, +	SET_KEY_REQ, +	AUTO_RATE_IND, +	BOOTUP_PARAMS_REQUEST, +	VAP_CAPABILITIES, +	EEPROM_READ_TYPE , +	EEPROM_WRITE, +	GPIO_PIN_CONFIG , +	SET_RX_FILTER, +	AMPDU_IND, +	STATS_REQUEST_FRAME, +	BB_BUF_PROG_VALUES_REQ, +	BBP_PROG_IN_TA, +	BG_SCAN_PARAMS, +	BG_SCAN_PROBE_REQ, +	CW_MODE_REQ, +	PER_CMD_PKT +}; + +struct rsi_mac_frame { +	__le16 desc_word[8]; +} __packed; + +struct rsi_boot_params { +	__le16 desc_word[8]; +	struct bootup_params bootup_params; +} __packed; + +struct rsi_peer_notify { +	__le16 desc_word[8]; +	u8 mac_addr[6]; +	__le16 command; +	__le16 mpdu_density; +	__le16 reserved; +	__le32 sta_flags; +} __packed; + +struct rsi_vap_caps { +	__le16 desc_word[8]; +	u8 mac_addr[6]; +	__le16 keep_alive_period; +	u8 bssid[6]; +	__le16 reserved; +	__le32 flags; +	__le16 frag_threshold; +	__le16 rts_threshold; +	__le32 default_mgmt_rate; +	__le32 default_ctrl_rate; +	__le32 default_data_rate; +	__le16 beacon_interval; +	__le16 dtim_period; +} __packed; + +struct rsi_set_key { +	__le16 desc_word[8]; +	u8 key[4][32]; +	u8 tx_mic_key[8]; +	u8 rx_mic_key[8]; +} __packed; + +struct rsi_auto_rate { +	__le16 desc_word[8]; +	__le16 failure_limit; +	__le16 initial_boundary; +	__le16 max_threshold_limt; +	__le16 num_supported_rates; +	__le16 aarf_rssi; +	__le16 moderate_rate_inx; +	__le16 collision_tolerance; +	__le16 supported_rates[40]; +} __packed; + +struct qos_params { +	__le16 cont_win_min_q; +	__le16 cont_win_max_q; +	__le16 aifsn_val_q; +	__le16 txop_q; +} __packed; + +struct rsi_radio_caps { +	__le16 desc_word[8]; +	struct qos_params qos_params[MAX_HW_QUEUES]; +	u8 num_11n_rates; +	u8 num_11ac_rates; +	__le16 gcpd_per_rate[20]; +} __packed; + +static inline u32 rsi_get_queueno(u8 *addr, u16 offset) +{ +	return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12; +} + +static inline u32 rsi_get_length(u8 *addr, u16 offset) +{ +	return (le16_to_cpu(*(__le16 *)&addr[offset])) & 0x0fff; +} + +static inline u8 rsi_get_extended_desc(u8 *addr, u16 offset) +{ +	return le16_to_cpu(*((__le16 *)&addr[offset + 4])) & 0x00ff; +} + +static inline u8 rsi_get_rssi(u8 *addr) +{ +	return *(u8 *)(addr + FRAME_DESC_SZ); +} + +static inline u8 rsi_get_channel(u8 *addr) +{ +	return *(char *)(addr + 15); +} + +int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg); +int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode); +int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, +				      u16 ssn, u8 buf_size, u8 event); +int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, +		     u8 key_type, u8 key_id, u32 cipher); +int rsi_set_channel(struct rsi_common *common, u16 chno); +void rsi_inform_bss_status(struct rsi_common *common, u8 status, +			   const u8 *bssid, u8 qos_enable, u16 aid); +void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb); +int rsi_mac80211_attach(struct rsi_common *common); +void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb, +			    int status); +bool rsi_is_cipher_wep(struct rsi_common *common); +void rsi_core_qos_processor(struct rsi_common *common); +void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb); +int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb); +int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); +#endif diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h new file mode 100644 index 00000000000..df4b5e20e05 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -0,0 +1,129 @@ +/** + * @section LICENSE + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_SDIO_INTF__ +#define __RSI_SDIO_INTF__ + +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio_ids.h> +#include "rsi_main.h" + +enum sdio_interrupt_type { +	BUFFER_FULL         = 0x0, +	BUFFER_AVAILABLE    = 0x1, +	FIRMWARE_ASSERT_IND = 0x3, +	MSDU_PACKET_PENDING = 0x4, +	UNKNOWN_INT         = 0XE +}; + +/* Buffer status register related info */ +#define PKT_BUFF_SEMI_FULL                      0 +#define PKT_BUFF_FULL                           1 +#define PKT_MGMT_BUFF_FULL                      2 +#define MSDU_PKT_PENDING                        3 +/* Interrupt Bit Related Macros */ +#define PKT_BUFF_AVAILABLE                      0 +#define FW_ASSERT_IND                           2 + +#define RSI_DEVICE_BUFFER_STATUS_REGISTER       0xf3 +#define RSI_FN1_INT_REGISTER                    0xf9 +#define RSI_SD_REQUEST_MASTER                   0x10000 + +/* FOR SD CARD ONLY */ +#define SDIO_RX_NUM_BLOCKS_REG                  0x000F1 +#define SDIO_FW_STATUS_REG                      0x000F2 +#define SDIO_NXT_RD_DELAY2                      0x000F5 +#define SDIO_MASTER_ACCESS_MSBYTE               0x000FA +#define SDIO_MASTER_ACCESS_LSBYTE               0x000FB +#define SDIO_READ_START_LVL                     0x000FC +#define SDIO_READ_FIFO_CTL                      0x000FD +#define SDIO_WRITE_FIFO_CTL                     0x000FE +#define SDIO_FUN1_INTR_CLR_REG                  0x0008 +#define SDIO_REG_HIGH_SPEED                     0x0013 + +#define RSI_GET_SDIO_INTERRUPT_TYPE(_I, TYPE)      \ +	{					   \ +		TYPE =                             \ +		(_I & (1 << PKT_BUFF_AVAILABLE)) ? \ +		BUFFER_AVAILABLE :		   \ +		(_I & (1 << MSDU_PKT_PENDING)) ?   \ +		MSDU_PACKET_PENDING :              \ +		(_I & (1 << FW_ASSERT_IND)) ?      \ +		FIRMWARE_ASSERT_IND : UNKNOWN_INT; \ +	} + +/* common registers in SDIO function1 */ +#define TA_SOFT_RESET_REG            0x0004 +#define TA_TH0_PC_REG                0x0400 +#define TA_HOLD_THREAD_REG           0x0844 +#define TA_RELEASE_THREAD_REG        0x0848 + +#define TA_SOFT_RST_CLR              0 +#define TA_SOFT_RST_SET              BIT(0) +#define TA_PC_ZERO                   0 +#define TA_HOLD_THREAD_VALUE         cpu_to_le32(0xF) +#define TA_RELEASE_THREAD_VALUE      cpu_to_le32(0xF) +#define TA_BASE_ADDR                 0x2200 +#define MISC_CFG_BASE_ADDR           0x4150 + +struct receive_info { +	bool buffer_full; +	bool semi_buffer_full; +	bool mgmt_buffer_full; +	u32 mgmt_buf_full_counter; +	u32 buf_semi_full_counter; +	u8 watch_bufferfull_count; +	u32 sdio_intr_status_zero; +	u32 sdio_int_counter; +	u32 total_sdio_msdu_pending_intr; +	u32 total_sdio_unknown_intr; +	u32 buf_full_counter; +	u32 buf_avilable_counter; +}; + +struct rsi_91x_sdiodev { +	struct sdio_func *pfunction; +	struct task_struct *in_sdio_litefi_irq; +	struct receive_info rx_info; +	u32 next_read_delay; +	u32 sdio_high_speed_enable; +	u8 sdio_clock_speed; +	u32 cardcapability; +	u8 prev_desc[16]; +	u32 tx_blk_size; +	u8 write_fail; +}; + +void rsi_interrupt_handler(struct rsi_hw *adapter); +int rsi_init_sdio_slave_regs(struct rsi_hw *adapter); +int rsi_sdio_device_init(struct rsi_common *common); +int rsi_sdio_read_register(struct rsi_hw *adapter, u32 addr, u8 *data); +int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, u8 *pkt, u32 length); +int rsi_sdio_write_register(struct rsi_hw *adapter, u8 function, +			    u32 addr, u8 *data); +int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, u32 addr, +				     u8 *data, u32 count); +void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit); +int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter); +int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num); +#endif diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h new file mode 100644 index 00000000000..ebea0c411ea --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -0,0 +1,68 @@ +/** + * @section LICENSE + * Copyright (c) 2014 Redpine Signals Inc. + * + * 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 __RSI_USB_INTF__ +#define __RSI_USB_INTF__ + +#include <linux/usb.h> +#include "rsi_main.h" +#include "rsi_common.h" + +#define USB_INTERNAL_REG_1           0x25000 +#define RSI_USB_READY_MAGIC_NUM      0xab +#define FW_STATUS_REG                0x41050012 + +#define USB_VENDOR_REGISTER_READ     0x15 +#define USB_VENDOR_REGISTER_WRITE    0x16 +#define RSI_USB_TX_HEAD_ROOM         128 + +#define MAX_RX_URBS                  1 +#define MAX_BULK_EP                  8 +#define MGMT_EP                      1 +#define DATA_EP                      2 + +struct rsi_91x_usbdev { +	struct rsi_thread rx_thread; +	u8 endpoint; +	struct usb_device *usbdev; +	struct usb_interface *pfunction; +	struct urb *rx_usb_urb[MAX_RX_URBS]; +	u8 *tx_buffer; +	__le16 bulkin_size; +	u8 bulkin_endpoint_addr; +	__le16 bulkout_size[MAX_BULK_EP]; +	u8 bulkout_endpoint_addr[MAX_BULK_EP]; +	u32 tx_blk_size; +	u8 write_fail; +}; + +static inline int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num) +{ +	/* In USB, there isn't any need to check the queue status */ +	return QUEUE_NOT_FULL; +} + +static inline int rsi_usb_event_timeout(struct rsi_hw *adapter) +{ +	return EVENT_WAIT_FOREVER; +} + +int rsi_usb_device_init(struct rsi_common *common); +int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr, +				    u8 *data, u32 count); +void rsi_usb_rx_thread(struct rsi_common *common); +#endif  | 
