int iwm_store_rxiq_calib_result(struct iwm_priv *iwm) { struct iwm_calib_rxiq *rxiq; u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); int grplen = sizeof(struct iwm_calib_rxiq_group); rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL); if (!rxiq) { IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n"); return -ENOMEM; } eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); if (IS_ERR(eeprom_rxiq)) { IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n"); return PTR_ERR(eeprom_rxiq); } iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq; iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq); rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD; rxiq->hdr.first_grp = 0; rxiq->hdr.grp_num = 1; rxiq->hdr.all_data_valid = 1; memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen); memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen); return 0; }
static int iwm_check_profile(struct iwm_priv *iwm) { if (!iwm->umac_profile_active) return -EAGAIN; if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 && iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP && iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) { IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n", iwm->umac_profile->sec.ucast_cipher); return -EAGAIN; } if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 && iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 && iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP && iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) { IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n", iwm->umac_profile->sec.mcast_cipher); return -EAGAIN; } if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 || iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) && (iwm->umac_profile->sec.ucast_cipher != iwm->umac_profile->sec.mcast_cipher)) { IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n"); } return 0; }
static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw, u16 op_code, u32 index) { int offset = -EINVAL, fw_offset; u32 op_index = 0; const u8 *fw_ptr; struct iwm_fw_hdr_rec *rec; fw_offset = 0; fw_ptr = fw->data; if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) { IWM_ERR(iwm, "No barker string in this FW\n"); return -EINVAL; } if (fw->size < IWM_HDR_LEN) { IWM_ERR(iwm, "FW is too small (%zu)\n", fw->size); return -EINVAL; } fw_offset += IWM_HDR_BARKER_LEN; while (fw_offset < fw->size) { rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset); IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n", rec->op_code, rec->len, fw_offset); if (rec->op_code == IWM_HDR_REC_OP_INVALID) { IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n"); break; } if (rec->op_code == op_code) { if (op_index == index) { fw_offset += sizeof(struct iwm_fw_hdr_rec); offset = fw_offset; goto out; } op_index++; } fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len; } out: return offset; }
static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg) { u8 *auth_type = &iwm->umac_profile->sec.auth_type; switch (auth_alg) { case IW_AUTH_ALG_OPEN_SYSTEM: *auth_type = UMAC_AUTH_TYPE_OPEN; break; case IW_AUTH_ALG_SHARED_KEY: if (iwm->umac_profile->sec.flags & (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) { if (*auth_type == UMAC_AUTH_TYPE_8021X) return -EINVAL; *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; } else { *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; } break; case IW_AUTH_ALG_LEAP: default: IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg); return -ENOTSUPP; } return 0; }
int iwm_send_pmkid_update(struct iwm_priv *iwm, struct cfg80211_pmksa *pmksa, u32 command) { struct iwm_umac_pmkid_update update; int ret; memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); update.hdr.oid = UMAC_WIFI_IF_CMD_PMKID_UPDATE; update.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pmkid_update) - sizeof(struct iwm_umac_wifi_if)); update.command = cpu_to_le32(command); if (pmksa->bssid) memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); if (pmksa->pmkid) memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); ret = iwm_send_wifi_if_cmd(iwm, &update, sizeof(struct iwm_umac_pmkid_update), 0); if (ret) { IWM_ERR(iwm, "PMKID update command failed\n"); return ret; } return 0; }
int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, struct iwm_umac_notif_stop_resume_tx *ntf) { struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; struct iwm_umac_cmd umac_cmd; struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; struct iwm_sta_info *sta_info; u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); int i; sta_info = &iwm->sta_table[sta_id]; if (!sta_info->valid) { IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); return -EINVAL; } umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; umac_cmd.resp = 0; stp_res_cmd.flags = ntf->flags; stp_res_cmd.sta_id = ntf->sta_id; stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; for (i = 0; i < IWM_UMAC_TID_NR; i++) stp_res_cmd.last_seq_num[i] = sta_info->tid_info[i].last_seq_num; return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, sizeof(struct iwm_umac_cmd_stop_resume_tx)); }
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, int ssid_num) { struct iwm_umac_cmd_scan_request req; int i, ret; memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request)); req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST; req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request) - sizeof(struct iwm_umac_wifi_if)); req.type = UMAC_WIFI_IF_SCAN_TYPE_USER; req.timeout = 2; req.seq_num = iwm->scan_id; req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX); for (i = 0; i < req.ssid_num; i++) { memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len); req.ssids[i].ssid_len = ssids[i].ssid_len; } ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); if (ret < 0) { IWM_ERR(iwm, "Couldn't send scan request\n"); return ret; } iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX; return 0; }
int iwm_eeprom_init(struct iwm_priv *iwm) { int i, ret = 0; char name[32]; iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL); if (!iwm->eeprom) return -ENOMEM; for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { ret = iwm_eeprom_read(iwm, i); if (ret < 0) { IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n", i, eeprom_map[i].name); break; } } IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n"); for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { memset(name, 0, 32); sprintf(name, "%s: ", eeprom_map[i].name); IWM_HEXDUMP(iwm, DBG, BOOT, name, iwm->eeprom + eeprom_map[i].offset, eeprom_map[i].length); } return ret; }
static int iwm_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct iwm_priv *iwm = ndev_to_iwm(dev); size_t len = data->length; int ret; if (iwm->conf.mode == UMAC_MODE_IBSS) return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); if (!test_bit(IWM_STATUS_READY, &iwm->status)) return -EIO; if (len > 0 && ssid[len - 1] == '\0') len--; if (iwm->umac_profile_active) { if (iwm->umac_profile->ssid.ssid_len == len && !memcmp(iwm->umac_profile->ssid.ssid, ssid, len)) return 0; ret = iwm_invalidate_mlme_profile(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't invalidate profile\n"); return ret; } } iwm->umac_profile->ssid.ssid_len = len; memcpy(iwm->umac_profile->ssid.ssid, ssid, len); return iwm_send_mlme_profile(iwm); }
static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast) { u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher : &iwm->umac_profile->sec.mcast_cipher; switch (cipher) { case IW_AUTH_CIPHER_NONE: *profile_cipher = UMAC_CIPHER_TYPE_NONE; break; case IW_AUTH_CIPHER_WEP40: *profile_cipher = UMAC_CIPHER_TYPE_WEP_40; break; case IW_AUTH_CIPHER_TKIP: *profile_cipher = UMAC_CIPHER_TYPE_TKIP; break; case IW_AUTH_CIPHER_CCMP: *profile_cipher = UMAC_CIPHER_TYPE_CCMP; break; case IW_AUTH_CIPHER_WEP104: *profile_cipher = UMAC_CIPHER_TYPE_WEP_104; break; default: IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher); return -ENOTSUPP; } return 0; }
int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, bool resp) { struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload; struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; struct iwm_umac_cmd umac_cmd; int ret; u8 oid = hdr->oid; if (!test_bit(IWM_STATUS_READY, &iwm->status)) { IWM_ERR(iwm, "Interface is not ready yet"); return -EAGAIN; } umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; umac_cmd.resp = resp; ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, payload, payload_size); if (resp) { ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue, test_and_clear_bit(oid, &iwm->wifi_ntfy[0]), 3 * HZ); return ret ? 0 : -EBUSY; } return ret; }
int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key, void *payload, u16 payload_size) { struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; struct iwm_umac_cmd umac_cmd; struct iwm_umac_cmd_set_param_var *param_hdr; u8 *param; int ret; param = kzalloc(payload_size + sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL); if (!param) { IWM_ERR(iwm, "Couldn't allocate param\n"); return -ENOMEM; } param_hdr = (struct iwm_umac_cmd_set_param_var *)param; umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR; umac_cmd.resp = 0; param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR); param_hdr->key = cpu_to_le16(key); param_hdr->len = cpu_to_le16(payload_size); memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var), payload, payload_size); ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param, sizeof(struct iwm_umac_cmd_set_param_var) + payload_size); kfree(param); return ret; }
int iwm_send_prio_table(struct iwm_priv *iwm) { struct iwm_coex_prio_table_cmd coex_table_cmd; u32 coex_enabled, mode_enabled; memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd)); coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; switch (iwm->conf.coexist_mode) { case COEX_MODE_XOR: case COEX_MODE_CM: coex_enabled = 1; break; default: coex_enabled = 0; break; } switch (iwm->conf.mode) { case UMAC_MODE_BSS: case UMAC_MODE_IBSS: mode_enabled = 1; break; default: mode_enabled = 0; break; } if (coex_enabled && mode_enabled) { coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK | COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; switch (iwm->conf.coexist_mode) { case COEX_MODE_XOR: memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, sizeof(iwm_sta_xor_prio_tbl)); break; case COEX_MODE_CM: memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl, sizeof(iwm_sta_cm_prio_tbl)); break; default: IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", iwm->conf.coexist_mode); break; } } else IWM_WARN(iwm, "coexistense disabled\n"); return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, &coex_table_cmd, sizeof(struct iwm_coex_prio_table_cmd), 1); }
static int iwm_target_read(struct iwm_priv *iwm, __le32 address, u8 *response, u32 resp_size) { struct iwm_udma_nonwifi_cmd target_cmd; struct iwm_nonwifi_cmd *cmd; u16 seq_num; int ret = 0; target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ; target_cmd.addr = address; target_cmd.op1_sz = cpu_to_le32(resp_size); target_cmd.op2 = 0; target_cmd.handle_by_hw = 0; target_cmd.resp = 1; target_cmd.eop = 1; ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); if (ret < 0) { IWM_ERR(iwm, "Couldn't send READ command\n"); return ret; } /* */ seq_num = ret; ret = wait_event_interruptible_timeout(iwm->nonwifi_queue, (cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num, UMAC_HDI_OUT_OPCODE_READ)) != NULL, 2 * HZ); if (!ret) { IWM_ERR(iwm, "Didn't receive a target READ answer\n"); return ret; } memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr), resp_size); kfree(cmd); return 0; }
/* Bus ops */ static int if_sdio_enable(struct iwm_priv *iwm) { struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); int ret; sdio_claim_host(hw->func); ret = sdio_enable_func(hw->func); if (ret) { IWM_ERR(iwm, "Couldn't enable the device: is TOP driver " "loaded and functional?\n"); goto release_host; } iwm_reset(iwm); ret = sdio_claim_irq(hw->func, iwm_sdio_isr); if (ret) { IWM_ERR(iwm, "Failed to claim irq: %d\n", ret); goto release_host; } sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret); if (ret < 0) { IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret); goto release_irq; } sdio_release_host(hw->func); IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n"); return 0; release_irq: sdio_release_irq(hw->func); release_host: sdio_release_host(hw->func); return ret; }
static int if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir) { int result; struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); hw->cccr_dentry = debugfs_create_file("cccr", 0200, parent_dir, iwm, &iwm_debugfs_sdio_fops); result = PTR_ERR(hw->cccr_dentry); if (IS_ERR(hw->cccr_dentry) && (result != -ENODEV)) { IWM_ERR(iwm, "Couldn't create CCCR entry: %d\n", result); return result; } return 0; }
static int iwm_load_firmware_chunk(struct iwm_priv *iwm, const struct firmware *fw, struct iwm_fw_img_desc *img_desc) { struct iwm_udma_nonwifi_cmd target_cmd; u32 chunk_size; const u8 *chunk_ptr; int ret = 0; IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n", img_desc->length, img_desc->address); target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; target_cmd.handle_by_hw = 1; target_cmd.op2 = 0; target_cmd.resp = 0; target_cmd.eop = 1; chunk_size = img_desc->length; chunk_ptr = fw->data + img_desc->offset; while (chunk_size > 0) { u32 tmp_chunk_size; tmp_chunk_size = min_t(u32, chunk_size, IWM_MAX_NONWIFI_CMD_BUFF_SIZE); target_cmd.addr = cpu_to_le32(img_desc->address + (chunk_ptr - fw->data - img_desc->offset)); target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size); IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n", tmp_chunk_size, target_cmd.addr); ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr); if (ret < 0) { IWM_ERR(iwm, "Couldn't load FW chunk\n"); break; } chunk_size -= tmp_chunk_size; chunk_ptr += tmp_chunk_size; } return ret; }
int iwm_send_mlme_profile(struct iwm_priv *iwm) { int ret, i; struct iwm_umac_profile profile; memcpy(&profile, iwm->umac_profile, sizeof(profile)); profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - sizeof(struct iwm_umac_wifi_if)); ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); if (ret < 0) { IWM_ERR(iwm, "Send profile command failed\n"); return ret; } /* Wait for the profile to be active */ ret = wait_event_interruptible_timeout(iwm->mlme_queue, iwm->umac_profile_active == 1, 3 * HZ); if (!ret) return -EBUSY; for (i = 0; i < IWM_NUM_KEYS; i++) if (iwm->keys[i].in_use) { int default_key = 0; struct iwm_key *key = &iwm->keys[i]; if (key == iwm->default_key) default_key = 1; /* Wait for the profile before sending the keys */ wait_event_interruptible_timeout(iwm->mlme_queue, (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) || test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)), 3 * HZ); ret = iwm_set_key(iwm, 0, default_key, key); if (ret < 0) return ret; } return 0; }
static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt) { u8 *auth_type = &iwm->umac_profile->sec.auth_type; if (key_mgt == IW_AUTH_KEY_MGMT_802_1X) *auth_type = UMAC_AUTH_TYPE_8021X; else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) { if (iwm->umac_profile->sec.flags & (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; else *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; } else { IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt); return -EINVAL; } return 0; }
static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count) { struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); int aligned_count = ALIGN(count, hw->blk_size); int ret; if ((unsigned long)buf & 0x3) { IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf); /* TODO: Is this a hardware limitation? use get_unligned */ return -EINVAL; } sdio_claim_host(hw->func); ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf, aligned_count); sdio_release_host(hw->func); return ret; }
static int iwm_wext_siwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); int ret; if ((data->flags) & (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT | IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) { /* We need to invalidate the current profile */ if (iwm->umac_profile_active) { ret = iwm_invalidate_mlme_profile(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't invalidate profile\n"); return ret; } } } switch (data->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: return iwm_set_wpa_version(iwm, data->value); break; case IW_AUTH_CIPHER_PAIRWISE: return iwm_set_cipher(iwm, data->value, 1); break; case IW_AUTH_CIPHER_GROUP: return iwm_set_cipher(iwm, data->value, 0); break; case IW_AUTH_KEY_MGMT: return iwm_set_key_mgt(iwm, data->value); break; case IW_AUTH_80211_AUTH_ALG: return iwm_set_auth_alg(iwm, data->value); break; default: return -ENOTSUPP; } return 0; }
int iwm_send_umac_channel_list(struct iwm_priv *iwm) { struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; struct iwm_umac_cmd umac_cmd; struct iwm_umac_cmd_get_channel_list *ch_list; int size = sizeof(struct iwm_umac_cmd_get_channel_list) + sizeof(struct iwm_umac_channel_info) * 4; int ret; ch_list = kzalloc(size, GFP_KERNEL); if (!ch_list) { IWM_ERR(iwm, "Couldn't allocate channel list cmd\n"); return -ENOMEM; } ch_list->ch[0].band = UMAC_BAND_2GHZ; ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ; ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID; ch_list->ch[1].band = UMAC_BAND_5GHZ; ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ; ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID; ch_list->ch[2].band = UMAC_BAND_2GHZ; ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ; ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; ch_list->ch[3].band = UMAC_BAND_5GHZ; ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ; ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; ch_list->count = cpu_to_le16(4); umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST; umac_cmd.resp = 1; ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size); kfree(ch_list); return ret; }
int iwm_send_mlme_profile(struct iwm_priv *iwm) { int ret; struct iwm_umac_profile profile; memcpy(&profile, iwm->umac_profile, sizeof(profile)); profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - sizeof(struct iwm_umac_wifi_if)); ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); if (ret) { IWM_ERR(iwm, "Send profile command failed\n"); return ret; } set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); return 0; }
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) { int ret; u8 mac_align[ALIGN(ETH_ALEN, 8)]; ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR), mac_align, sizeof(mac_align)); if (ret < 0) return ret; if (is_valid_ether_addr(mac_align)) memcpy(mac, mac_align, ETH_ALEN); else { IWM_ERR(iwm, "Invalid EEPROM MAC\n"); memcpy(mac, iwm->conf.mac_addr, ETH_ALEN); get_random_bytes(&mac[3], 3); } return 0; }
static int iwm_load_umac(struct iwm_priv *iwm) { struct iwm_udma_nonwifi_cmd target_cmd; int ret; ret = iwm_load_img(iwm, iwm->bus_ops->umac_name); if (ret < 0) return ret; target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP; target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR); target_cmd.op1_sz = 0; target_cmd.op2 = 0; target_cmd.handle_by_hw = 0; target_cmd.resp = 1 ; target_cmd.eop = 1; ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); if (ret < 0) IWM_ERR(iwm, "Couldn't send JMP command\n"); return ret; }
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct iwm_priv *iwm = ndev_to_iwm(netdev); struct wireless_dev *wdev = iwm_to_wdev(iwm); struct iwm_tx_info *tx_info; struct iwm_tx_queue *txq; struct iwm_sta_info *sta_info; u8 *dst_addr, sta_id; u16 queue; int ret; if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " "not associated\n"); netif_tx_stop_all_queues(netdev); goto drop; } queue = skb_get_queue_mapping(skb); BUG_ON(queue >= IWM_TX_DATA_QUEUES); txq = &iwm->txq[queue]; if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) || (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); netif_stop_subqueue(netdev, queue); return NETDEV_TX_BUSY; } ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype, iwm->bssid, 0); if (ret) { IWM_ERR(iwm, "build wifi header failed\n"); goto drop; } dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1; for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) { sta_info = &iwm->sta_table[sta_id]; if (sta_info->valid && !memcmp(dst_addr, sta_info->addr, ETH_ALEN)) break; } if (sta_id == IWM_STA_TABLE_NUM) { IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n", dst_addr); goto drop; } tx_info = skb_to_tx_info(skb); tx_info->sta = sta_id; tx_info->color = sta_info->color; if (sta_info->qos) tx_info->tid = skb->priority; else tx_info->tid = IWM_UMAC_MGMT_TID; spin_lock_bh(&iwm->txq[queue].lock); skb_queue_tail(&iwm->txq[queue].queue, skb); spin_unlock_bh(&iwm->txq[queue].lock); queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; return NETDEV_TX_OK; drop: netdev->stats.tx_dropped++; dev_kfree_skb_any(skb); return NETDEV_TX_OK; }
void iwm_tx_worker(struct work_struct *work) { struct iwm_priv *iwm; struct iwm_tx_info *tx_info = NULL; struct sk_buff *skb; struct iwm_tx_queue *txq; struct iwm_sta_info *sta_info; struct iwm_tid_info *tid_info; int cmdlen, ret, pool_id; txq = container_of(work, struct iwm_tx_queue, worker); iwm = container_of(txq, struct iwm_priv, txq[txq->id]); pool_id = queue_to_pool_id(txq->id); while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && !skb_queue_empty(&txq->queue)) { spin_lock_bh(&txq->lock); skb = skb_dequeue(&txq->queue); spin_unlock_bh(&txq->lock); tx_info = skb_to_tx_info(skb); sta_info = &iwm->sta_table[tx_info->sta]; if (!sta_info->valid) { IWM_ERR(iwm, "Trying to send a frame to unknown STA\n"); kfree_skb(skb); continue; } tid_info = &sta_info->tid_info[tx_info->tid]; mutex_lock(&tid_info->mutex); if (tid_info->stopped) { IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n", tx_info->sta, tx_info->tid); spin_lock_bh(&txq->lock); skb_queue_tail(&txq->stopped_queue, skb); spin_unlock_bh(&txq->lock); mutex_unlock(&tid_info->mutex); continue; } cmdlen = IWM_UDMA_HDR_LEN + skb->len; IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " "%d, color: %d\n", txq->id, skb, tx_info->sta, tx_info->color); if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE) iwm_tx_send_concat_packets(iwm, txq); ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen); if (ret) { IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " "%d, Tx worker stopped\n", txq->id); spin_lock_bh(&txq->lock); skb_queue_head(&txq->queue, skb); spin_unlock_bh(&txq->lock); mutex_unlock(&tid_info->mutex); break; } txq->concat_ptr = txq->concat_buf + txq->concat_count; tid_info->last_seq_num = iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); txq->concat_count += ALIGN(cmdlen, 16); mutex_unlock(&tid_info->mutex); kfree_skb(skb); } iwm_tx_send_concat_packets(iwm, txq); if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) && !test_bit(pool_id, &iwm->tx_credit.full_pools_map) && (skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) { IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id); netif_wake_subqueue(iwm_to_ndev(iwm), txq->id); } }
static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { struct iwm_priv *iwm = filp->private_data; struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); char *buf; u8 cccr; int buf_len = 4096, ret; size_t len = 0; if (*ppos != 0) return 0; if (count < sizeof(buf)) return -ENOSPC; buf = kzalloc(buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; sdio_claim_host(hw->func); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr); cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret); if (ret) { IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n"); goto err; } len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr); ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); err: sdio_release_host(hw->func); kfree(buf); return ret; }
static void iwm_sdio_isr(struct sdio_func *func) { struct iwm_priv *iwm; struct iwm_sdio_priv *hw; struct iwm_rx_info *rx_info; struct sk_buff *skb; unsigned long buf_size, read_size; int ret; u8 val; hw = sdio_get_drvdata(func); iwm = hw_to_iwm(hw); buf_size = hw->blk_size; /* We're checking the status */ val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret); if (val == 0 || ret < 0) { IWM_ERR(iwm, "Wrong INTR_STATUS\n"); return; } /* See if we have free buffers */ if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) { IWM_ERR(iwm, "No buffer for more Rx frames\n"); return; } /* We first read the transaction size */ read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret); read_size = read_size << 8; if (ret < 0) { IWM_ERR(iwm, "Couldn't read the xfer size\n"); return; } /* We need to clear the INT register */ sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret); if (ret < 0) { IWM_ERR(iwm, "Couldn't clear the INT register\n"); return; } while (buf_size < read_size) buf_size <<= 1; skb = dev_alloc_skb(buf_size); if (!skb) { IWM_ERR(iwm, "Couldn't alloc RX skb\n"); return; } rx_info = skb_to_rx_info(skb); rx_info->rx_size = read_size; rx_info->rx_buf_size = buf_size; /* Now we can read the actual buffer */ ret = sdio_memcpy_fromio(func, skb_put(skb, read_size), IWM_SDIO_DATA_ADDR, read_size); /* The skb is put on a driver's specific Rx SKB list */ skb_queue_tail(&iwm->rx_list, skb); /* We can now schedule the actual worker */ queue_work(hw->isr_wq, &hw->isr_worker); }
static int iwm_wext_siwencodeext(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); struct iwm_key *key; struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; int uninitialized_var(alg), idx, i, remove = 0; IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg); IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len); IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags); IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags); IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length); switch (ext->alg) { case IW_ENCODE_ALG_NONE: remove = 1; break; case IW_ENCODE_ALG_WEP: if (ext->key_len == WLAN_KEY_LEN_WEP40) alg = UMAC_CIPHER_TYPE_WEP_40; else if (ext->key_len == WLAN_KEY_LEN_WEP104) alg = UMAC_CIPHER_TYPE_WEP_104; else { IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len); return -EINVAL; } break; case IW_ENCODE_ALG_TKIP: alg = UMAC_CIPHER_TYPE_TKIP; break; case IW_ENCODE_ALG_CCMP: alg = UMAC_CIPHER_TYPE_CCMP; break; default: return -EOPNOTSUPP; } idx = erq->flags & IW_ENCODE_INDEX; if (idx == 0) { if (iwm->default_key) for (i = 0; i < IWM_NUM_KEYS; i++) { if (iwm->default_key == &iwm->keys[i]) { idx = i; break; } } } else if (idx < 1 || idx > 4) { return -EINVAL; } else idx--; if (erq->flags & IW_ENCODE_DISABLED) remove = 1; else if ((erq->length == 0) || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { iwm->default_key = &iwm->keys[idx]; if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP) return iwm_set_tx_key(iwm, idx); } key = iwm_key_init(iwm, idx, !remove, ext, alg); return iwm_set_key(iwm, remove, !iwm->default_key, key); }