Esempio n. 1
0
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;
}
Esempio n. 2
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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
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;
}
Esempio n. 6
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));

}
Esempio n. 7
0
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;
}
Esempio n. 8
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;
}
Esempio n. 9
0
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);
}
Esempio n. 10
0
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;
}
Esempio n. 11
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;
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
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);
}
Esempio n. 14
0
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;
}
Esempio n. 15
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;
}
Esempio n. 16
0
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;
}
Esempio n. 17
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;
}
Esempio n. 18
0
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;
}
Esempio n. 19
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;
}
Esempio n. 20
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;
}
Esempio n. 21
0
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;
}
Esempio n. 22
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;
}
Esempio n. 23
0
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;
}
Esempio n. 24
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;
}
Esempio n. 25
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;
}
Esempio n. 26
0
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;
}
Esempio n. 27
0
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);
	}
}
Esempio n. 28
0
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;
}
Esempio n. 29
0
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);
}
Esempio n. 30
0
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);
}