| /*HEADER********************************************************************** |
| ****************************************************************************** |
| *** |
| *** Copyright (c) 2011, 2012, Imagination Technologies Ltd. |
| *** |
| *** This program is free software; you can redistribute it and/or |
| *** modify it under the terms of the GNU General Public License |
| *** as published by the Free Software Foundation; either version 2 |
| *** of the License, or (at your option) any later version. |
| *** |
| *** This program is distributed in the hope that it will be useful, |
| *** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| *** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| *** GNU General Public License for more details. |
| *** |
| *** You should have received a copy of the GNU General Public License |
| *** along with this program; if not, write to the Free Software |
| *** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
| *** USA. |
| *** |
| *** File Name : lmac_if.c |
| *** |
| *** File Description: |
| *** This file contains the defintions of helper functions for LMAC comms |
| *** |
| ****************************************************************************** |
| *END**************************************************************************/ |
| #include <linux/spinlock.h> |
| #include <linux/rcupdate.h> |
| #include <linux/slab.h> |
| #include <linux/netdevice.h> |
| |
| #include "lmac_if.h" |
| |
| #ifdef CONFIG_LMACIF_DEBUG |
| #define LMACIF_DEBUG(fmt, args...) printk(KERN_DEBUG fmt, ##args) |
| #else |
| #define LMACIF_DEBUG(...) do { } while (0) |
| #endif |
| |
| struct lmac_if_data { |
| char *name; |
| void *context; |
| }; |
| |
| static struct lmac_if_data __rcu *lmac_if; |
| |
| static int uccp310wlan_send_cmd(unsigned char *buf, |
| unsigned int len) |
| { |
| struct umac_lmac_msg_hdr *hdr = (struct umac_lmac_msg_hdr *)buf; |
| struct sk_buff *nbuf; |
| unsigned char *data; |
| struct lmac_if_data *p; |
| |
| rcu_read_lock(); |
| p = (struct lmac_if_data *)(rcu_dereference(lmac_if)); |
| if (!p) { |
| WARN_ON(1); |
| rcu_read_unlock(); |
| return -1; |
| } |
| |
| nbuf = alloc_skb(len, GFP_ATOMIC); |
| if (!nbuf) { |
| rcu_read_unlock(); |
| return -1; |
| } |
| |
| /* LMACIF_DEBUG ("%s-LMACIF: Sending command:%d\n", p->name, hdr->id); */ |
| hdr->flags = (len - sizeof(struct umac_lmac_msg_hdr)); |
| |
| data = skb_put(nbuf, len); |
| memcpy(data, buf, len); |
| |
| hal_ops.send((void *)nbuf, LMAC_MOD_ID, UMAC_MOD_ID); |
| rcu_read_unlock(); |
| return 0; |
| } |
| |
| int uccp310wlan_prog_reset(unsigned int reset_type) |
| { |
| struct umac_cmd_reset reset; |
| |
| reset.hdr.id = CMD_RESET; |
| reset.hdr.flags = 0; |
| reset.reset_type = reset_type; |
| return uccp310wlan_send_cmd((unsigned char *) &reset, |
| sizeof(struct umac_cmd_reset)); |
| } |
| |
| int uccp310wlan_prog_vif_ctrl(int index, |
| unsigned char *mac_addr, |
| unsigned int vif_type, |
| unsigned int op) |
| { |
| struct umac_cmd_vif_ctrl vif_ctrl; |
| |
| vif_ctrl.hdr.id = CMD_VIF_CTRL; |
| vif_ctrl.hdr.flags = 0; |
| vif_ctrl.if_mode = vif_type; |
| memcpy(vif_ctrl.mac_addr, mac_addr, 6); |
| vif_ctrl.if_index = index; |
| vif_ctrl.if_ctrl = op; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &vif_ctrl, |
| sizeof(struct umac_cmd_vif_ctrl)); |
| } |
| |
| int uccp310wlan_prog_vif_basic_rates(int index, |
| unsigned char *vif_addr, |
| unsigned int basic_rate_set) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = BASICRATES_CHANGED; |
| vif_cfg.basic_rate_set = basic_rate_set; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_short_slot(int index, |
| unsigned char *vif_addr, |
| unsigned int use_short_slot) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = SHORTSLOT_CHANGED; |
| vif_cfg.use_short_slot = use_short_slot; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| |
| int uccp310wlan_prog_vif_powersave_mode(int index, |
| unsigned char *vif_addr, |
| unsigned int powersave_mode) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| vif_cfg.changed_bitmap = POWERSAVE_CHANGED; |
| vif_cfg.powersave_mode = powersave_mode; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_atim_window(int index, |
| unsigned char *vif_addr, |
| unsigned int atim_window) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = ATIMWINDOW_CHANGED; |
| vif_cfg.atim_window = atim_window; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_aid(int index, |
| unsigned char *vif_addr, |
| unsigned int aid) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = AID_CHANGED; |
| vif_cfg.aid = aid; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| |
| int uccp310wlan_prog_vif_assoc_cap(int index, |
| unsigned char *vif_addr, |
| unsigned int caps) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = CAPABILITY_CHANGED; |
| vif_cfg.capability = caps; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_apsd_type(int index, |
| unsigned char *vif_addr, |
| unsigned int uapsd_type) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = UAPSDTYPE_CHANGED; |
| vif_cfg.uapsd_type = uapsd_type; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_long_retry(int index, |
| unsigned char *vif_addr, |
| unsigned int long_retry) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = LONGRETRY_CHANGED; |
| vif_cfg.long_retry = long_retry; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| int uccp310wlan_prog_vif_short_retry(int index, |
| unsigned char *vif_addr, |
| unsigned int short_retry) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = SHORTRETRY_CHANGED; |
| vif_cfg.short_retry = short_retry; |
| vif_cfg.if_index = index; |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| |
| int uccp310wlan_prog_vif_bssid(int index, |
| unsigned char *vif_addr, |
| unsigned char *bssid) |
| { |
| struct umac_cmd_vif_cfg vif_cfg; |
| |
| vif_cfg.hdr.id = CMD_VIF_CFG; |
| vif_cfg.hdr.flags = 0; |
| |
| vif_cfg.changed_bitmap = BSSID_CHANGED; |
| memcpy(vif_cfg.bssid, bssid, 6); |
| memcpy(vif_cfg.vif_addr, vif_addr, 6); |
| vif_cfg.if_index = index; |
| return uccp310wlan_send_cmd((unsigned char *)&vif_cfg, |
| sizeof(struct umac_cmd_vif_cfg)); |
| } |
| |
| int uccp310wlan_prog_powersave_state(int index, |
| unsigned char *vif_addr, |
| unsigned int powersave_state) |
| { |
| struct umac_cmd_ps_cfg ps_cfg; |
| |
| ps_cfg.hdr.id = CMD_PS_CFG; |
| ps_cfg.hdr.flags = 0; |
| |
| ps_cfg.powersave_state = powersave_state; |
| ps_cfg.if_index = index; |
| memcpy(ps_cfg.vif_addr, vif_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *)&ps_cfg, |
| sizeof(struct umac_cmd_ps_cfg)); |
| } |
| |
| int uccp310wlan_prog_global_cfg(unsigned int rx_msdu_lifetime, |
| unsigned int tx_msdu_lifetime, |
| unsigned int sensitivity, |
| unsigned int dyn_ed_enable, |
| unsigned char *rf_params) |
| { |
| struct umac_cmd_global_cfg gbl_config; |
| |
| gbl_config.hdr.id = CMD_GLOBAL_CFG; |
| gbl_config.hdr.flags = 0; |
| |
| gbl_config.rx_msdu_lifetime = rx_msdu_lifetime; |
| gbl_config.tx_msdu_lifetime = tx_msdu_lifetime; |
| gbl_config.ed_sensitivity = sensitivity; |
| gbl_config.dynamic_ed_enable = dyn_ed_enable; |
| memcpy(gbl_config.rf_params, rf_params, 8); |
| |
| return uccp310wlan_send_cmd((unsigned char *) &gbl_config, |
| sizeof(struct umac_cmd_global_cfg)); |
| } |
| |
| int uccp310wlan_prog_txpower(unsigned int txpower) |
| { |
| struct umac_cmd_txpower power; |
| |
| power.hdr.id = CMD_TX_POWER; |
| power.hdr.flags = 0; |
| |
| power.txpower = txpower; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &power, |
| sizeof(struct umac_cmd_txpower)); |
| } |
| |
| int uccp310wlan_prog_mcast_addr_cfg(unsigned char *mcast_addr, |
| unsigned int op) |
| { |
| struct umac_cmd_mcst_filter_cfg mcast_config; |
| |
| mcast_config.hdr.id = CMD_MCST_FILTER_CFG; |
| mcast_config.hdr.flags = 0; |
| mcast_config.mcst_ctrl = op; |
| memcpy(mcast_config.addr, mcast_addr, 6); |
| |
| return uccp310wlan_send_cmd((unsigned char *) &mcast_config, |
| sizeof(struct umac_cmd_mcst_filter_cfg)); |
| } |
| |
| int uccp310wlan_prog_mcast_filter_control(unsigned int mcast_filter_enable) |
| { |
| struct umac_cmd_mcst_filter_ctrl mcast_ctrl; |
| |
| mcast_ctrl.hdr.id = CMD_MCST_FILTER_CTRL; |
| mcast_ctrl.hdr.flags = 0; |
| mcast_ctrl.ctrl = mcast_filter_enable; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &mcast_ctrl, |
| sizeof(struct umac_cmd_mcst_filter_ctrl)); |
| } |
| |
| int uccp310wlan_prog_rcv_bcn_mode(unsigned int bcn_rcv_mode) |
| { |
| struct umac_cmd_rcv_bcn_mode rcv_bcn_mode; |
| |
| rcv_bcn_mode.hdr.id = CMD_RCV_BCN_MODE; |
| rcv_bcn_mode.hdr.flags = 0; |
| rcv_bcn_mode.mode = bcn_rcv_mode; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &rcv_bcn_mode, |
| sizeof(struct umac_cmd_rcv_bcn_mode)); |
| } |
| |
| int uccp310wlan_prog_txq_params(int index, |
| unsigned char *addr, |
| unsigned int queue, |
| unsigned int aifs, |
| unsigned int txop, |
| unsigned int cwmin, |
| unsigned int cwmax) |
| { |
| struct umac_cmd_txq_params params; |
| |
| params.hdr.id = CMD_TXQ_PARAMS; |
| params.hdr.flags = 0; |
| |
| params.if_index = index; |
| memcpy(params.vif_addr, addr, ETH_ALEN); |
| params.queue_num = queue; |
| params.aifsn = aifs; |
| params.txop = txop; |
| params.cwmin = cwmin; |
| params.cwmax = cwmax; |
| |
| return uccp310wlan_send_cmd((unsigned char *) ¶ms, |
| sizeof(struct umac_cmd_txq_params)); |
| } |
| |
| int uccp310wlan_prog_channel(unsigned int ch) |
| { |
| struct umac_cmd_channel channel; |
| |
| channel.hdr.id = CMD_CHANNEL; |
| channel.hdr.flags = 0; |
| channel.channel = ch; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &channel, |
| sizeof(struct umac_cmd_channel)); |
| } |
| |
| int uccp310wlan_prog_peer_key(int vif_index, |
| unsigned char *vif_addr, |
| unsigned int op, |
| unsigned int key_id, |
| unsigned int key_type, |
| unsigned int cipher_type, |
| struct umac_key *key) |
| { |
| struct umac_cmd_peer_key_cfg peer_key; |
| |
| peer_key.hdr.id = CMD_PEER_KEY_CFG; |
| peer_key.hdr.flags = 0; |
| |
| peer_key.if_index = vif_index; |
| memcpy(peer_key.vif_addr, vif_addr, ETH_ALEN); |
| peer_key.op = op; |
| peer_key.key_id = key_id; |
| memcpy(peer_key.peer_mac, key->peer_mac, ETH_ALEN); |
| |
| peer_key.key_type = key_type; |
| peer_key.cipher_type = cipher_type; |
| memcpy(peer_key.key, key->key, MAX_KEY_LEN); |
| if (key->tx_mic) |
| memcpy(peer_key.tx_mic, key->tx_mic, MICHAEL_LEN); |
| if (key->rx_mic) |
| memcpy(peer_key.rx_mic, key->rx_mic, MICHAEL_LEN); |
| return uccp310wlan_send_cmd((unsigned char *) &peer_key, |
| sizeof(struct umac_cmd_peer_key_cfg)); |
| } |
| |
| int uccp310wlan_prog_if_key(int vif_index, |
| unsigned char *vif_addr, |
| unsigned int op, |
| unsigned int key_id, |
| unsigned int cipher_type, |
| struct umac_key *key) |
| { |
| struct umac_cmd_if_key_cfg if_key; |
| |
| if_key.hdr.id = CMD_IF_KEY_CFG; |
| if_key.hdr.flags = 0; |
| |
| if_key.if_index = vif_index; |
| memcpy(if_key.vif_addr, vif_addr, 6); |
| if_key.key_id = key_id; |
| if_key.op = op; |
| |
| if (op == KEY_CTRL_ADD) { |
| if_key.cipher_type = cipher_type; |
| if (cipher_type == CIPHER_TYPE_TKIP || cipher_type == CIPHER_TYPE_CCMP) { |
| memcpy(if_key.key.rsn_grp_key.key, key->key, MAX_KEY_LEN); |
| if (key->tx_mic) |
| memcpy(if_key.key.rsn_grp_key.mic_key, key->tx_mic, MICHAEL_LEN); |
| } else { |
| if_key.key.wep_key.key_len = |
| (cipher_type == CIPHER_TYPE_WEP40) ? 5 : 13; |
| memcpy(if_key.key.wep_key.wep_key, key->key, |
| if_key.key.wep_key.key_len); |
| } |
| } |
| return uccp310wlan_send_cmd((unsigned char *) &if_key, |
| sizeof(struct umac_cmd_if_key_cfg)); |
| } |
| |
| int uccp310wlan_prog_tx(struct sk_buff *skb) |
| { |
| struct umac_cmd_tx *tx_cmd; |
| struct lmac_if_data *p; |
| tx_cmd = (struct umac_cmd_tx *)skb->data; |
| tx_cmd->hdr.id = CMD_TX; |
| tx_cmd->hdr.flags = ((sizeof(struct umac_cmd_tx) - sizeof(struct umac_lmac_msg_hdr)) | ((skb->len - sizeof(struct umac_cmd_tx)) << 16)); |
| |
| rcu_read_lock(); |
| |
| p = (struct lmac_if_data *)(rcu_dereference(lmac_if)); |
| |
| if (!p) { |
| WARN_ON(1); |
| rcu_read_unlock(); |
| return -1; |
| } |
| |
| hal_ops.send((void *)skb, LMAC_MOD_ID, UMAC_MOD_ID); |
| |
| rcu_read_unlock(); |
| return 0; |
| } |
| |
| int uccp310wlan_prog_mib_stats(void) |
| { |
| struct umac_cmd_mib_stats mib_stats; |
| |
| mib_stats.hdr.id = CMD_MIB_STATS; |
| mib_stats.hdr.flags = 0; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &mib_stats, |
| sizeof(struct umac_cmd_mib_stats)); |
| } |
| |
| int uccp310wlan_prog_phy_stats(void) |
| { |
| struct umac_cmd_phy_stats phy_stats; |
| |
| phy_stats.hdr.id = CMD_PHY_STATS; |
| phy_stats.hdr.flags = 0; |
| |
| return uccp310wlan_send_cmd((unsigned char *) &phy_stats, |
| sizeof(struct umac_cmd_phy_stats)); |
| } |
| |
| static int uccp310wlan_msg_handler (void *nbuff, |
| unsigned char sender_id) |
| { |
| unsigned int event; |
| unsigned char *buff; |
| struct umac_lmac_msg_hdr *hdr; |
| struct lmac_if_data *p; |
| struct sk_buff *skb = (struct sk_buff *)nbuff; |
| |
| rcu_read_lock(); |
| |
| p = (struct lmac_if_data *)(rcu_dereference(lmac_if)); |
| |
| if (!p) { |
| WARN_ON(1); |
| dev_kfree_skb_any(skb); |
| rcu_read_unlock(); |
| return 0; |
| } |
| |
| buff = skb->data; |
| hdr = (struct umac_lmac_msg_hdr *)buff; |
| |
| hdr->id &= 0x0000ffff; |
| hdr->flags &= 0x0000ffff; |
| |
| event = hdr->id; |
| |
| /* LMACIF_DEBUG("%s-LMACIF: event %d received\n", p->name, event); */ |
| if (event == EVENT_RESET_COMPLETE) { |
| struct umac_event_reset_complete *r = (struct umac_event_reset_complete *)buff; |
| uccp310wlan_reset_complete(r->version, p->context); |
| |
| } else if (event == EVENT_RX /* || event == EVENT_RX_MIC_FAILURE*/) { |
| uccp310wlan_rx_frame(nbuff, p->context); |
| |
| } else if (event == EVENT_TX_DONE) { |
| uccp310wlan_tx_complete((struct umac_event_tx_done *)buff, p->context); |
| |
| } else if (event == EVENT_MIB_STAT) { |
| struct umac_event_mib_stats *mib_stats = (struct umac_event_mib_stats *) buff; |
| uccp310wlan_mib_stats(mib_stats, p->context); |
| |
| } else if (event == EVENT_NOA) { |
| uccp310wlan_noa_event(event, (void *)buff, p->context, NULL); |
| |
| } else { |
| /* |
| * TODO:: handle remaining events |
| */ |
| } |
| |
| if (event != EVENT_RX) |
| dev_kfree_skb_any(skb); |
| |
| rcu_read_unlock(); |
| return 0; |
| } |
| |
| int uccp310wlan_lmac_if_init(void *context, const char *name) |
| { |
| struct lmac_if_data *p; |
| LMACIF_DEBUG("%s-LMACIF: lmac_if init called\n", name); |
| p = kzalloc(sizeof(struct lmac_if_data), GFP_KERNEL); |
| |
| if (!p) |
| return -ENOMEM; |
| |
| p->name = (char *)name; |
| p->context = context; |
| hal_ops.register_callback(uccp310wlan_msg_handler, UMAC_MOD_ID); |
| rcu_assign_pointer(lmac_if, p); |
| return 0; |
| } |
| |
| void uccp310wlan_lmac_if_deinit(void) |
| { |
| struct lmac_if_data *p; |
| LMACIF_DEBUG("%s-LMACIF: Deinit called\n", lmac_if->name); |
| p = rcu_dereference(lmac_if); |
| rcu_assign_pointer(lmac_if, NULL); |
| synchronize_rcu(); |
| kfree(p); |
| } |