/* * IOCTL request handler to set/reset WPA IE. * * The supplied WPA IE is treated as a opaque buffer. Only the first field * is checked to determine WPA version. If buffer length is zero, the existing * WPA IE is reset. */ static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > sizeof(priv->wpa_ie)) { mwifiex_dbg(priv->adapter, ERROR, "failed to copy WPA IE, too big\n"); return -1; } memcpy(priv->wpa_ie, ie_data_ptr, ie_len); priv->wpa_ie_len = ie_len; mwifiex_dbg(priv->adapter, CMD, "cmd: Set Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { priv->sec_info.wpa_enabled = true; } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { priv->sec_info.wpa2_enabled = true; } else { priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; } } else { memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); priv->wpa_ie_len = 0; mwifiex_dbg(priv->adapter, INFO, "info: reset wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; } return 0; }
/* * IOCTL request handler to set/get generic IE. * * In addition to various generic IEs, this function can also be * used to set the ARP filter. */ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, struct mwifiex_ds_misc_gen_ie *gen_ie, u16 action) { struct mwifiex_adapter *adapter = priv->adapter; switch (gen_ie->type) { case MWIFIEX_IE_TYPE_GEN_IE: if (action == HostCmd_ACT_GEN_GET) { gen_ie->len = priv->wpa_ie_len; memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); } else { mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, (u16) gen_ie->len); } break; case MWIFIEX_IE_TYPE_ARP_FILTER: memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { adapter->arp_filter_size = 0; mwifiex_dbg(adapter, ERROR, "invalid ARP filter size\n"); return -1; } else { memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len); adapter->arp_filter_size = gen_ie->len; } break; default: mwifiex_dbg(adapter, ERROR, "invalid IE type\n"); return -1; } return 0; }
/* * IOCTL request handler to set/reset WPS IE. * * The supplied WPS IE is treated as a opaque buffer. Only the first field * is checked to internally enable WPS. If buffer length is zero, the existing * WPS IE is reset. */ static int mwifiex_set_wps_ie(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > MWIFIEX_MAX_VSIE_LEN) { mwifiex_dbg(priv->adapter, ERROR, "info: failed to copy WPS IE, too big\n"); return -1; } priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); if (!priv->wps_ie) return -ENOMEM; memcpy(priv->wps_ie, ie_data_ptr, ie_len); priv->wps_ie_len = ie_len; mwifiex_dbg(priv->adapter, CMD, "cmd: Set wps_ie_len=%d IE=%#x\n", priv->wps_ie_len, priv->wps_ie[0]); } else { kfree(priv->wps_ie); priv->wps_ie_len = ie_len; mwifiex_dbg(priv->adapter, INFO, "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); } return 0; }
/* This function prepares channel report request command to FW for * starting radar detection. */ int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, void *data_buf) { struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; struct mwifiex_radar_params *radar_params = (void *)data_buf; cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); cmd->size = cpu_to_le16(S_DS_GEN); le16_unaligned_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; cr_req->chan_desc.chan_width = radar_params->chandef->width; cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); if (radar_params->cac_time_ms) mwifiex_dbg(priv->adapter, MSG, "11h: issuing DFS Radar check for channel=%d\n", radar_params->chandef->chan->hw_value); else mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); return 0; }
/* * This function allocates buffers for members of the adapter * structure. * * The memory allocated includes scan table, command buffers, and * sleep confirm command buffer. In addition, the queues are * also initialized. */ static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) { int ret; /* Allocate command buffer */ ret = mwifiex_alloc_cmd_buffer(adapter); if (ret) { mwifiex_dbg(adapter, ERROR, "%s: failed to alloc cmd buffer\n", __func__); return -1; } adapter->sleep_cfm = dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + INTF_HEADER_LEN); if (!adapter->sleep_cfm) { mwifiex_dbg(adapter, ERROR, "%s: failed to alloc sleep cfm\t" " cmd buffer\n", __func__); return -1; } skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); return 0; }
/* * This function handles the command response of set rf antenna */ static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; struct mwifiex_adapter *adapter = priv->adapter; if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) { priv->tx_ant = le16_to_cpu(ant_mimo->tx_ant_mode); priv->rx_ant = le16_to_cpu(ant_mimo->rx_ant_mode); mwifiex_dbg(adapter, INFO, "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" "Rx action = 0x%x, Rx Mode = 0x%04x\n", le16_to_cpu(ant_mimo->action_tx), le16_to_cpu(ant_mimo->tx_ant_mode), le16_to_cpu(ant_mimo->action_rx), le16_to_cpu(ant_mimo->rx_ant_mode)); } else { priv->tx_ant = le16_to_cpu(ant_siso->ant_mode); priv->rx_ant = le16_to_cpu(ant_siso->ant_mode); mwifiex_dbg(adapter, INFO, "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", le16_to_cpu(ant_siso->action), le16_to_cpu(ant_siso->ant_mode)); } return 0; }
/* This is work queue function for channel switch handling. * This function takes care of updating new channel definitin to * bss config structure, restart AP and indicate channel switch success * to cfg80211. */ void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) { struct mwifiex_uap_bss_param *bss_cfg; struct delayed_work *delayed_work = to_delayed_work(work); struct mwifiex_private *priv = container_of(delayed_work, struct mwifiex_private, dfs_chan_sw_work); bss_cfg = &priv->bss_cfg; if (!bss_cfg->beacon_period) { mwifiex_dbg(priv->adapter, ERROR, "channel switch: AP already stopped\n"); return; } mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); if (mwifiex_config_start_uap(priv, bss_cfg)) { mwifiex_dbg(priv->adapter, ERROR, "Failed to start AP after channel switch\n"); return; } mwifiex_dbg(priv->adapter, MSG, "indicating channel switch completion to kernel\n"); cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); }
/* * IOCTL request handler to set/reset WAPI IE. * * The supplied WAPI IE is treated as a opaque buffer. Only the first field * is checked to internally enable WAPI. If buffer length is zero, the existing * WAPI IE is reset. */ static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, u8 *ie_data_ptr, u16 ie_len) { if (ie_len) { if (ie_len > sizeof(priv->wapi_ie)) { mwifiex_dbg(priv->adapter, ERROR, "info: failed to copy WAPI IE, too big\n"); return -1; } memcpy(priv->wapi_ie, ie_data_ptr, ie_len); priv->wapi_ie_len = ie_len; mwifiex_dbg(priv->adapter, CMD, "cmd: Set wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, priv->wapi_ie[0]); if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) priv->sec_info.wapi_enabled = true; } else { memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); priv->wapi_ie_len = ie_len; mwifiex_dbg(priv->adapter, INFO, "info: Reset wapi_ie_len=%d IE=%#x\n", priv->wapi_ie_len, priv->wapi_ie[0]); priv->sec_info.wapi_enabled = false; } return 0; }
/* Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a * 'host sleep activate' request to the firmware and turns off the traffic. */ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; struct usb_tx_data_port *port; int i, j; /* Might still be loading firmware */ wait_for_completion(&card->fw_done); adapter = card->adapter; if (!adapter) { dev_err(&intf->dev, "card is not valid\n"); return 0; } if (unlikely(adapter->is_suspended)) mwifiex_dbg(adapter, WARN, "Device already suspended\n"); /* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) { mwifiex_dbg(adapter, ERROR, "cmd: failed to suspend\n"); adapter->hs_enabling = false; return -EFAULT; } /* 'is_suspended' flag indicates device is suspended. * It must be set here before the usb_kill_urb() calls. Reason * is in the complete handlers, urb->status(= -ENOENT) and * this flag is used in combination to distinguish between a * 'suspended' state and a 'disconnect' one. */ adapter->is_suspended = true; adapter->hs_enabling = false; if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); if (atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) if (card->rx_data_list[i].urb) usb_kill_urb(card->rx_data_list[i].urb); for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { port = &card->port[i]; for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { if (port->tx_data_list[j].urb) usb_kill_urb(port->tx_data_list[j].urb); } } if (card->tx_cmd.urb) usb_kill_urb(card->tx_cmd.urb); return 0; }
static void mwifiex_fw_dump_info_event(struct mwifiex_private *priv, struct sk_buff *event_skb) { struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_fw_dump_header *fw_dump_hdr = (void *)adapter->event_body; if (adapter->iface_type != MWIFIEX_USB) { mwifiex_dbg(adapter, MSG, "event is not on usb interface, ignore it\n"); return; } if (!adapter->devdump_data) { /* When receive the first event, allocate device dump * buffer, dump driver info. */ adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); if (!adapter->devdump_data) { mwifiex_dbg(adapter, ERROR, "vzalloc devdump data failure!\n"); return; } mwifiex_drv_info_dump(adapter); /* If no proceeded event arrive in 10s, upload device * dump data, this will be useful if the end of * transmission event get lost, in this cornel case, * user would still get partial of the dump. */ mod_timer(&adapter->devdump_timer, jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); } /* Overflow check */ if (adapter->devdump_len + event_skb->len >= MWIFIEX_FW_DUMP_SIZE) goto upload_dump; memmove(adapter->devdump_data + adapter->devdump_len, adapter->event_skb->data, event_skb->len); adapter->devdump_len += event_skb->len; if (le16_to_cpu(fw_dump_hdr->type == FW_DUMP_INFO_ENDED)) { mwifiex_dbg(adapter, MSG, "receive end of transmission flag event!\n"); goto upload_dump; } return; upload_dump: del_timer_sync(&adapter->devdump_timer); mwifiex_upload_device_dump(adapter); }
/* * This function handles the command response error case. * * For scan response error, the function cancels all the pending * scan commands and generates an event to inform the applications * of the scan completion. * * For Power Save command failure, we do not retry enter PS * command in case of Ad-hoc mode. * * For all other response errors, the current command buffer is freed * and returned to the free command queue. */ static void mwifiex_process_cmdresp_error(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ps_mode_enh *pm; unsigned long flags; mwifiex_dbg(adapter, ERROR, "CMD_RESP: cmd %#x error, result=%#x\n", resp->command, resp->result); if (adapter->curr_cmd->wait_q_enabled) adapter->cmd_wait_q.status = -1; switch (le16_to_cpu(resp->command)) { case HostCmd_CMD_802_11_PS_MODE_ENH: pm = &resp->params.psmode_enh; mwifiex_dbg(adapter, ERROR, "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", resp->result, le16_to_cpu(pm->action)); /* We do not re-try enter-ps command in ad-hoc mode. */ if (le16_to_cpu(pm->action) == EN_AUTO_PS && (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && priv->bss_mode == NL80211_IFTYPE_ADHOC) adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; break; case HostCmd_CMD_802_11_SCAN: case HostCmd_CMD_802_11_SCAN_EXT: mwifiex_cancel_pending_scan_cmd(adapter); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); break; case HostCmd_CMD_MAC_CONTROL: break; case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: mwifiex_dbg(adapter, MSG, "SDIO RX single-port aggregation Not support\n"); break; default: break; } /* Handling errors here */ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd = NULL; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); }
static int mwifiex_process_country_ie(struct mwifiex_private *priv, struct cfg80211_bss *bss) { const u8 *country_ie; u8 country_ie_len; struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg; rcu_read_lock(); country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); if (!country_ie) { rcu_read_unlock(); return 0; } country_ie_len = country_ie[1]; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { rcu_read_unlock(); return 0; } if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { rcu_read_unlock(); mwifiex_dbg(priv->adapter, INFO, "11D: skip setting domain info in FW\n"); return 0; } memcpy(priv->adapter->country_code, &country_ie[2], 2); domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; domain_info->country_code[2] = ' '; country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; domain_info->no_of_triplet = country_ie_len / sizeof(struct ieee80211_country_ie_triplet); memcpy((u8 *)domain_info->triplet, &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); rcu_read_unlock(); if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, NULL, false)) { mwifiex_dbg(priv->adapter, ERROR, "11D: setting domain info in FW fail\n"); return -1; } mwifiex_dnld_txpwr_table(priv); return 0; }
/* * This function downloads the firmware to the card. * * The actual download is preceded by two sanity checks - * - Check if firmware is already running * - Check if the interface is the winner to download the firmware * * ...and followed by another - * - Check if the firmware is downloaded successfully * * After download is successfully completed, the host interrupts are enabled. */ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *pmfw) { int ret; u32 poll_num = 1; if (adapter->if_ops.check_fw_status) { /* check if firmware is already running */ ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (!ret) { mwifiex_dbg(adapter, MSG, "WLAN FW already running! Skip FW dnld\n"); return 0; } } /* check if we are the winner for downloading FW */ if (adapter->if_ops.check_winner_status) { adapter->winner = 0; ret = adapter->if_ops.check_winner_status(adapter); poll_num = MAX_FIRMWARE_POLL_TRIES; if (ret) { mwifiex_dbg(adapter, MSG, "WLAN read winner status failed!\n"); return ret; } if (!adapter->winner) { mwifiex_dbg(adapter, MSG, "WLAN is not the winner! Skip FW dnld\n"); goto poll_fw; } } if (pmfw) { /* Download firmware with helper */ ret = adapter->if_ops.prog_fw(adapter, pmfw); if (ret) { mwifiex_dbg(adapter, ERROR, "prog_fw failed ret=%#x\n", ret); return ret; } } poll_fw: /* Check if the firmware is downloaded successfully or not */ ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (ret) mwifiex_dbg(adapter, ERROR, "FW failed to be active in time\n"); return ret; }
/* This function is to abort ongoing CAC upon stopping AP operations * or during unload. */ void mwifiex_abort_cac(struct mwifiex_private *priv) { if (priv->wdev.cac_started) { if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) mwifiex_dbg(priv->adapter, ERROR, "failed to stop CAC in FW\n"); mwifiex_dbg(priv->adapter, MSG, "Aborting delayed work for CAC.\n"); cancel_delayed_work_sync(&priv->dfs_cac_work); cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); } }
/* * This function handles the command response of set/get Tx power * configurations. * * Handling includes changing the header fields into CPU format * and saving the current Tx power level in driver. */ static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; struct mwifiex_types_power_group *pg_tlv_hdr; struct mwifiex_power_group *pg; u16 action = le16_to_cpu(txp_cfg->action); u16 tlv_buf_left; pg_tlv_hdr = (struct mwifiex_types_power_group *) ((u8 *)txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); pg = (struct mwifiex_power_group *) ((u8 *)pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); if (tlv_buf_left < le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) return 0; switch (action) { case HostCmd_ACT_GEN_GET: if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_get_power_level(priv, pg_tlv_hdr); priv->tx_power_level = (u16) pg->power_min; break; case HostCmd_ACT_GEN_SET: if (!le32_to_cpu(txp_cfg->mode)) break; if (pg->power_max == pg->power_min) priv->tx_power_level = (u16) pg->power_min; break; default: mwifiex_dbg(adapter, ERROR, "CMD_RESP: unknown cmd action %d\n", action); return 0; } mwifiex_dbg(adapter, INFO, "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", priv->tx_power_level, priv->max_tx_power_level, priv->min_tx_power_level); return 0; }
static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; u16 reason = le16_to_cpu(cmd_tdls_oper->reason); u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); switch (action) { case ACT_TDLS_DELETE: if (reason) { if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) mwifiex_dbg(priv->adapter, MSG, "TDLS link delete for %pM failed: reason %d\n", cmd_tdls_oper->peer_mac, reason); else mwifiex_dbg(priv->adapter, ERROR, "TDLS link delete for %pM failed: reason %d\n", cmd_tdls_oper->peer_mac, reason); } else { mwifiex_dbg(priv->adapter, MSG, "TDLS link delete for %pM successful\n", cmd_tdls_oper->peer_mac); } break; case ACT_TDLS_CREATE: if (reason) { mwifiex_dbg(priv->adapter, ERROR, "TDLS link creation for %pM failed: reason %d", cmd_tdls_oper->peer_mac, reason); if (node && reason != TDLS_ERR_LINK_EXISTS) node->tdls_status = TDLS_SETUP_FAILURE; } else { mwifiex_dbg(priv->adapter, MSG, "TDLS link creation for %pM successful", cmd_tdls_oper->peer_mac); } break; case ACT_TDLS_CONFIG: if (reason) { mwifiex_dbg(priv->adapter, ERROR, "TDLS link config for %pM failed, reason %d\n", cmd_tdls_oper->peer_mac, reason); if (node) node->tdls_status = TDLS_SETUP_FAILURE; } else { mwifiex_dbg(priv->adapter, MSG, "TDLS link config for %pM successful\n", cmd_tdls_oper->peer_mac); } break; default: mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS command action response %d", action); return -1; } return 0; }
/* * This function handles the command response of set/get v2 key material. * * Handling includes updating the driver parameters to reflect the * changes. */ static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_key_material_v2 *key_v2; __le16 len; key_v2 = &resp->params.key_material_v2; if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); priv->wpa_is_gtk_set = true; priv->scan_block = false; priv->port_open = true; } } if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) return 0; memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, WLAN_KEY_LEN_CCMP); priv->aes_key_v2.key_param_set.key_params.aes.key_len = key_v2->key_param_set.key_params.aes.key_len; len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); return 0; }
static void mwifiex_usb_disconnect(struct usb_interface *intf) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; wait_for_completion(&card->fw_done); adapter = card->adapter; if (!adapter || !adapter->priv_num) return; if (card->udev->state != USB_STATE_NOTATTACHED && !adapter->mfg_mode) { mwifiex_deauthenticate_all(adapter); mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_FUNC_SHUTDOWN); } mwifiex_usb_free(card); mwifiex_dbg(adapter, FATAL, "%s: removing card\n", __func__); mwifiex_remove_card(adapter); usb_put_dev(interface_to_usbdev(intf)); }
static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, struct mwifiex_ie_types_header *tlv) { struct mwifiex_tx_pause_tlv *tp; struct mwifiex_sta_node *sta_ptr; unsigned long flags; tp = (void *)tlv; mwifiex_dbg(priv->adapter, EVENT, "uap tx_pause: %pM pause=%d, pkts=%d\n", tp->peermac, tp->tx_pause, tp->pkt_cnt); if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { if (tp->tx_pause) priv->port_open = false; else priv->port_open = true; } else if (is_multicast_ether_addr(tp->peermac)) { mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); } else { spin_lock_irqsave(&priv->sta_list_spinlock, flags); sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { sta_ptr->tx_pause = tp->tx_pause; mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); } spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); } }
int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, unsigned int duration) { struct host_cmd_ds_remain_on_chan roc_cfg; u8 sc; memset(&roc_cfg, 0, sizeof(roc_cfg)); roc_cfg.action = cpu_to_le16(action); if (action == HostCmd_ACT_GEN_SET) { roc_cfg.band_cfg = chan->band; sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); roc_cfg.band_cfg |= (sc << 2); roc_cfg.channel = ieee80211_frequency_to_channel(chan->center_freq); roc_cfg.duration = cpu_to_le32(duration); } if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, action, 0, &roc_cfg, true)) { mwifiex_dbg(priv->adapter, ERROR, "failed to remain on channel\n"); return -1; } return roc_cfg.status; }
/* Proc hscfg file write handler * This function can be used to configure the host sleep parameters. */ static ssize_t mwifiex_hscfg_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct mwifiex_private *priv = (void *)file->private_data; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); int ret, arg_num; struct mwifiex_ds_hs_cfg hscfg; int conditions = HS_CFG_COND_DEF; u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; if (!buf) return -ENOMEM; if (copy_from_user(buf, ubuf, buf_size)) { ret = -EFAULT; goto done; } arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); if (arg_num > 3) { mwifiex_dbg(priv->adapter, ERROR, "Too many arguments\n"); ret = -EINVAL; goto done; } if (arg_num >= 1 && arg_num < 3) mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, MWIFIEX_SYNC_CMD, &hscfg); if (arg_num) { if (conditions == HS_CFG_CANCEL) { mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); ret = count; goto done; } hscfg.conditions = conditions; } if (arg_num >= 2) hscfg.gpio = gpio; if (arg_num == 3) hscfg.gap = gap; hscfg.is_invoke_hostcmd = false; mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, &hscfg); mwifiex_enable_hs(priv->adapter); priv->adapter->hs_enabling = false; ret = count; done: free_page(addr); return ret; }
/* * Add buffer into wmm tx queue and queue work to transmit it. */ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) { struct netdev_queue *txq; int index = mwifiex_1d_to_wmm_queue[skb->priority]; if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { txq = netdev_get_tx_queue(priv->netdev, index); if (!netif_tx_queue_stopped(txq)) { netif_tx_stop_queue(txq); mwifiex_dbg(priv->adapter, DATA, "stop queue: %d\n", index); } } if (mwifiex_bypass_tx_queue(priv, skb)) { atomic_inc(&priv->adapter->tx_pending); atomic_inc(&priv->adapter->bypass_tx_pending); mwifiex_wmm_add_buf_bypass_txqueue(priv, skb); } else { atomic_inc(&priv->adapter->tx_pending); mwifiex_wmm_add_buf_txqueue(priv, skb); } mwifiex_queue_main_work(priv->adapter); return 0; }
/* This function prepares the AP specific commands before sending them * to the firmware. * This is a generic function which calls specific command preparation * routines based upon the command number. */ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, u16 cmd_action, u32 type, void *data_buf, void *cmd_buf) { struct host_cmd_ds_command *cmd = cmd_buf; switch (cmd_no) { case HostCmd_CMD_UAP_SYS_CONFIG: if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) return -1; break; case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_STOP: case HOST_CMD_APCMD_SYS_RESET: case HOST_CMD_APCMD_STA_LIST: cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); break; case HostCmd_CMD_UAP_STA_DEAUTH: if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) return -1; break; case HostCmd_CMD_CHAN_REPORT_REQUEST: if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, data_buf)) return -1; break; default: mwifiex_dbg(priv->adapter, ERROR, "PREP_CMD: unknown cmd %#x\n", cmd_no); return -1; } return 0; }
static int mwifiex_usb_construct_send_urb(struct mwifiex_adapter *adapter, struct usb_tx_data_port *port, u8 ep, struct urb_context *context, struct sk_buff *skb_send) { struct usb_card_rec *card = adapter->card; int ret = -EINPROGRESS; struct urb *tx_urb; context->adapter = adapter; context->ep = ep; context->skb = skb_send; tx_urb = context->urb; if (ep == card->tx_cmd_ep && card->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) usb_fill_int_urb(tx_urb, card->udev, usb_sndintpipe(card->udev, ep), skb_send->data, skb_send->len, mwifiex_usb_tx_complete, (void *)context, card->tx_cmd_interval); else usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), skb_send->data, skb_send->len, mwifiex_usb_tx_complete, (void *)context); tx_urb->transfer_flags |= URB_ZERO_PACKET; if (ep == card->tx_cmd_ep) atomic_inc(&card->tx_cmd_urb_pending); else atomic_inc(&port->tx_data_urb_pending); if (ep != card->tx_cmd_ep && atomic_read(&port->tx_data_urb_pending) == MWIFIEX_TX_DATA_URB) { port->block_status = true; adapter->data_sent = mwifiex_usb_data_sent(adapter); ret = -ENOSR; } if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, "%s: usb_submit_urb failed\n", __func__); if (ep == card->tx_cmd_ep) { atomic_dec(&card->tx_cmd_urb_pending); } else { atomic_dec(&port->tx_data_urb_pending); port->block_status = false; adapter->data_sent = false; if (port->tx_data_ix) port->tx_data_ix--; else port->tx_data_ix = MWIFIEX_TX_DATA_URB; } ret = -1; } return ret; }
/* This function handles channel report event from FW during CAC period. * If radar is detected during CAC, driver indicates the same to cfg80211 * and also cancels ongoing delayed work. */ int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, struct sk_buff *skb) { struct host_cmd_ds_chan_rpt_event *rpt_event; struct mwifiex_ie_types_chan_rpt_data *rpt; u8 *evt_buf; u16 event_len, tlv_len; rpt_event = (void *)(skb->data + sizeof(u32)); event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ sizeof(u32)); if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { mwifiex_dbg(priv->adapter, ERROR, "Error in channel report event\n"); return -1; } evt_buf = (void *)&rpt_event->tlvbuf; while (event_len >= sizeof(struct mwifiex_ie_types_header)) { rpt = (void *)&rpt_event->tlvbuf; tlv_len = le16_to_cpu(rpt->header.len); switch (le16_to_cpu(rpt->header.type)) { case TLV_TYPE_CHANRPT_11H_BASIC: if (rpt->map.radar) { mwifiex_dbg(priv->adapter, MSG, "RADAR Detected on channel %d!\n", priv->dfs_chandef.chan->hw_value); cancel_delayed_work_sync(&priv->dfs_cac_work); cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, NL80211_RADAR_DETECTED, GFP_KERNEL); } break; default: break; } evt_buf += (tlv_len + sizeof(rpt->header)); event_len -= (tlv_len + sizeof(rpt->header)); } return 0; }
/* * This function prepares the correct firmware command and * issues it to set the multicast list. * * This function can be used to enable promiscuous mode, or enable all * multicast packets, or to enable selective multicast. */ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, struct mwifiex_multicast_list *mcast_list) { int ret = 0; u16 old_pkt_filter; old_pkt_filter = priv->curr_pkt_filter; if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { mwifiex_dbg(priv->adapter, INFO, "info: Enable Promiscuous mode\n"); priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; } else { /* Multicast */ priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { mwifiex_dbg(priv->adapter, INFO, "info: Enabling All Multicast!\n"); priv->curr_pkt_filter |= HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; } else { priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; mwifiex_dbg(priv->adapter, INFO, "info: Set multicast list=%d\n", mcast_list->num_multicast_addr); /* Send multicast addresses to firmware */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_MULTICAST_ADR, HostCmd_ACT_GEN_SET, 0, mcast_list, false); } } mwifiex_dbg(priv->adapter, INFO, "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", old_pkt_filter, priv->curr_pkt_filter); if (old_pkt_filter != priv->curr_pkt_filter) { ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, HostCmd_ACT_GEN_SET, 0, &priv->curr_pkt_filter, false); } return ret; }
static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) { struct mwifiex_adapter *adapter = ctx->adapter; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; if (card->rx_cmd_ep != ctx->ep) { ctx->skb = dev_alloc_skb(size); if (!ctx->skb) { mwifiex_dbg(adapter, ERROR, "%s: dev_alloc_skb failed\n", __func__); return -ENOMEM; } } if (card->rx_cmd_ep == ctx->ep && card->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT) usb_fill_int_urb(ctx->urb, card->udev, usb_rcvintpipe(card->udev, ctx->ep), ctx->skb->data, size, mwifiex_usb_rx_complete, (void *)ctx, card->rx_cmd_interval); else usb_fill_bulk_urb(ctx->urb, card->udev, usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, size, mwifiex_usb_rx_complete, (void *)ctx); if (card->rx_cmd_ep == ctx->ep) atomic_inc(&card->rx_cmd_urb_pending); else atomic_inc(&card->rx_data_urb_pending); if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); dev_kfree_skb_any(ctx->skb); ctx->skb = NULL; if (card->rx_cmd_ep == ctx->ep) atomic_dec(&card->rx_cmd_urb_pending); else atomic_dec(&card->rx_data_urb_pending); return -1; } return 0; }
/* * IOCTL request handler to set WPA key. * * This function prepares the correct firmware command and * issues it, after validation checks. * * Current driver only supports key length of up to 32 bytes. * * This function can also be used to disable a currently set key. */ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { int ret; u8 remove_key = false; struct host_cmd_ds_802_11_key_material *ibss_key; /* Current driver only supports key length of up to 32 bytes */ if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { mwifiex_dbg(priv->adapter, ERROR, "key length too long\n"); return -1; } if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { /* * IBSS/WPA-None uses only one key (Group) for both receiving * and sending unicast and multicast packets. */ /* Send the key as PTK to firmware */ encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, encrypt_key, false); if (ret) return ret; ibss_key = &priv->aes_key; memset(ibss_key, 0, sizeof(struct host_cmd_ds_802_11_key_material)); /* Copy the key in the driver */ memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, encrypt_key->key_len); memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, sizeof(ibss_key->key_param_set.key_len)); ibss_key->key_param_set.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP); ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); /* Send the key as GTK to firmware */ encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; } if (!encrypt_key->key_index) encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; if (remove_key) ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, !KEY_INFO_ENABLED, encrypt_key, true); else ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, encrypt_key, true); return ret; }
/* * This function handles the command response of set/get SNMP * MIB parameters. * * Handling includes changing the header fields into CPU format * and saving the parameter in driver. * * The following parameters are supported - * - Fragmentation threshold * - RTS threshold * - Short retry limit */ static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, u32 *data_buf) { struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; u16 oid = le16_to_cpu(smib->oid); u16 query_type = le16_to_cpu(smib->query_type); u32 ul_temp; mwifiex_dbg(priv->adapter, INFO, "info: SNMP_RESP: oid value = %#x,\t" "query_type = %#x, buf size = %#x\n", oid, query_type, le16_to_cpu(smib->buf_size)); if (query_type == HostCmd_ACT_GEN_GET) { ul_temp = get_unaligned_le16(smib->value); if (data_buf) *data_buf = ul_temp; switch (oid) { case FRAG_THRESH_I: mwifiex_dbg(priv->adapter, INFO, "info: SNMP_RESP: FragThsd =%u\n", ul_temp); break; case RTS_THRESH_I: mwifiex_dbg(priv->adapter, INFO, "info: SNMP_RESP: RTSThsd =%u\n", ul_temp); break; case SHORT_RETRY_LIM_I: mwifiex_dbg(priv->adapter, INFO, "info: SNMP_RESP: TxRetryCount=%u\n", ul_temp); break; case DTIM_PERIOD_I: mwifiex_dbg(priv->adapter, INFO, "info: SNMP_RESP: DTIM period=%u\n", ul_temp); default: break; } } return 0; }
/* This function handles the command response of set_cfg_data */ static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { if (resp->result != HostCmd_RESULT_OK) { mwifiex_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); return -1; } return 0; }