/** * 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; struct ieee80211_tx_info *info; struct skb_info *tx_params; struct ieee80211_bss_conf *bss; int status; u8 ieee80211_size = MIN_802_11_HDR_LEN; u8 extnd_size; __le16 *frame_desc; u16 seq_num; info = IEEE80211_SKB_CB(skb); bss = &info->control.vif->bss_conf; tx_params = (struct skb_info *)info->driver_data; if (!bss->assoc) { status = -EINVAL; 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); if (conf_is_ht40(&common->priv->hw->conf)) frame_desc[5] = cpu_to_le16(FULL40M_ENABLE); if (common->vif_info[0].sgi) { if (common->min_rate & 0x100) /* Only MCS rates */ frame_desc[4] |= cpu_to_le16(ENABLE_SHORTGI_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; struct ieee80211_tx_info *info; struct ieee80211_bss_conf *bss; struct ieee80211_hw *hw = adapter->hw; struct ieee80211_conf *conf = &hw->conf; struct skb_info *tx_params; int status = -E2BIG; __le16 *msg; u8 extnd_size; 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 == NL80211_BAND_2GHZ) msg[4] = cpu_to_le16(RSI_11B_MODE); else msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE); if (conf_is_ht40(conf)) { msg[4] = cpu_to_le16(0xB | RSI_11G_MODE); msg[5] = cpu_to_le16(0x6); } /* 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; }