diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/cmd.c')
| -rw-r--r-- | drivers/net/wireless/wl12xx/cmd.c | 169 | 
1 files changed, 130 insertions, 39 deletions
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 25990bd38be6..3414fc11e9ba 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -459,23 +459,39 @@ out:  int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)  { +	unsigned long flags;  	u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);  	if (link >= WL12XX_MAX_LINKS)  		return -EBUSY; +	/* these bits are used by op_tx */ +	spin_lock_irqsave(&wl->wl_lock, flags);  	__set_bit(link, wl->links_map);  	__set_bit(link, wlvif->links_map); +	spin_unlock_irqrestore(&wl->wl_lock, flags);  	*hlid = link;  	return 0;  }  void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)  { +	unsigned long flags; +  	if (*hlid == WL12XX_INVALID_LINK_ID)  		return; +	/* these bits are used by op_tx */ +	spin_lock_irqsave(&wl->wl_lock, flags);  	__clear_bit(*hlid, wl->links_map);  	__clear_bit(*hlid, wlvif->links_map); +	spin_unlock_irqrestore(&wl->wl_lock, flags); + +	/* +	 * At this point op_tx() will not add more packets to the queues. We +	 * can purge them. +	 */ +	wl1271_tx_reset_link_queues(wl, *hlid); +  	*hlid = WL12XX_INVALID_LINK_ID;  } @@ -515,7 +531,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,  			goto out_free;  	}  	cmd->device.hlid = wlvif->dev_hlid; -	cmd->device.session = wlvif->session_counter; +	cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);  	wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",  		     cmd->role_id, cmd->device.hlid, cmd->device.session); @@ -566,7 +582,7 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,  		goto out_free;  	} -	ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID); +	ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID);  	if (ret < 0) {  		wl1271_error("cmd role stop dev event completion error");  		goto out_free; @@ -715,6 +731,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)  	cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);  	cmd->ap.dtim_interval = bss_conf->dtim_period;  	cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; +	/* FIXME: Change when adding DFS */ +	cmd->ap.reset_tsf = 1;  /* By default reset AP TSF */  	cmd->channel = wlvif->channel;  	if (!bss_conf->hidden_ssid) { @@ -994,7 +1012,7 @@ out:  }  int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, -		       u8 ps_mode) +		       u8 ps_mode, u16 auto_ps_timeout)  {  	struct wl1271_cmd_ps_params *ps_params = NULL;  	int ret = 0; @@ -1009,6 +1027,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,  	ps_params->role_id = wlvif->role_id;  	ps_params->ps_mode = ps_mode; +	ps_params->auto_ps_timeout = auto_ps_timeout;  	ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,  			      sizeof(*ps_params), 0); @@ -1022,13 +1041,15 @@ out:  	return ret;  } -int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, -			    void *buf, size_t buf_len, int index, u32 rates) +int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, +			    u16 template_id, void *buf, size_t buf_len, +			    int index, u32 rates)  {  	struct wl1271_cmd_template_set *cmd;  	int ret = 0; -	wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id); +	wl1271_debug(DEBUG_CMD, "cmd template_set %d (role %d)", +		     template_id, role_id);  	WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE);  	buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); @@ -1039,6 +1060,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,  		goto out;  	} +	/* during initialization wlvif is NULL */ +	cmd->role_id = role_id;  	cmd->len = cpu_to_le16(buf_len);  	cmd->template_type = template_id;  	cmd->enabled_rates = cpu_to_le32(rates); @@ -1082,7 +1105,8 @@ int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif)  		ptr = skb->data;  	} -	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0, +	ret = wl1271_cmd_template_set(wl, wlvif->role_id, +				      CMD_TEMPL_NULL_DATA, ptr, size, 0,  				      wlvif->basic_rate);  out: @@ -1105,7 +1129,7 @@ int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,  	if (!skb)  		goto out; -	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, +	ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_KLV,  				      skb->data, skb->len,  				      CMD_TEMPL_KLV_IDX_NULL_DATA,  				      wlvif->basic_rate); @@ -1130,7 +1154,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,  	if (!skb)  		goto out; -	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, +	ret = wl1271_cmd_template_set(wl, wlvif->role_id, +				      CMD_TEMPL_PS_POLL, skb->data,  				      skb->len, 0, wlvif->basic_rate_set);  out: @@ -1138,9 +1163,10 @@ out:  	return ret;  } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, +int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, +			       u8 role_id, u8 band,  			       const u8 *ssid, size_t ssid_len, -			       const u8 *ie, size_t ie_len, u8 band) +			       const u8 *ie, size_t ie_len)  {  	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);  	struct sk_buff *skb; @@ -1158,10 +1184,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,  	rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);  	if (band == IEEE80211_BAND_2GHZ) -		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, +		ret = wl1271_cmd_template_set(wl, role_id, +					      CMD_TEMPL_CFG_PROBE_REQ_2_4,  					      skb->data, skb->len, 0, rate);  	else -		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, +		ret = wl1271_cmd_template_set(wl, role_id, +					      CMD_TEMPL_CFG_PROBE_REQ_5,  					      skb->data, skb->len, 0, rate);  out: @@ -1186,10 +1214,12 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,  	rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);  	if (wlvif->band == IEEE80211_BAND_2GHZ) -		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, +		ret = wl1271_cmd_template_set(wl, wlvif->role_id, +					      CMD_TEMPL_CFG_PROBE_REQ_2_4,  					      skb->data, skb->len, 0, rate);  	else -		ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, +		ret = wl1271_cmd_template_set(wl, wlvif->role_id, +					      CMD_TEMPL_CFG_PROBE_REQ_5,  					      skb->data, skb->len, 0, rate);  	if (ret < 0) @@ -1199,32 +1229,34 @@ out:  	return skb;  } -int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, -			     __be32 ip_addr) +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)  { -	int ret; +	int ret, extra; +	u16 fc;  	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); -	struct wl12xx_arp_rsp_template tmpl; +	struct sk_buff *skb; +	struct wl12xx_arp_rsp_template *tmpl;  	struct ieee80211_hdr_3addr *hdr;  	struct arphdr *arp_hdr; -	memset(&tmpl, 0, sizeof(tmpl)); +	skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) + +			    WL1271_EXTRA_SPACE_MAX); +	if (!skb) { +		wl1271_error("failed to allocate buffer for arp rsp template"); +		return -ENOMEM; +	} -	/* mac80211 header */ -	hdr = &tmpl.hdr; -	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | -					 IEEE80211_STYPE_DATA | -					 IEEE80211_FCTL_TODS); -	memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); -	memcpy(hdr->addr2, vif->addr, ETH_ALEN); -	memset(hdr->addr3, 0xff, ETH_ALEN); +	skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX); + +	tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl)); +	memset(tmpl, 0, sizeof(tmpl));  	/* llc layer */ -	memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); -	tmpl.llc_type = cpu_to_be16(ETH_P_ARP); +	memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); +	tmpl->llc_type = cpu_to_be16(ETH_P_ARP);  	/* arp header */ -	arp_hdr = &tmpl.arp_hdr; +	arp_hdr = &tmpl->arp_hdr;  	arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);  	arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);  	arp_hdr->ar_hln = ETH_ALEN; @@ -1232,13 +1264,59 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,  	arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);  	/* arp payload */ -	memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN); -	tmpl.sender_ip = ip_addr; +	memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN); +	tmpl->sender_ip = wlvif->ip_addr; -	ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, -				      &tmpl, sizeof(tmpl), 0, -				      wlvif->basic_rate); +	/* encryption space */ +	switch (wlvif->encryption_type) { +	case KEY_TKIP: +		extra = WL1271_EXTRA_SPACE_TKIP; +		break; +	case KEY_AES: +		extra = WL1271_EXTRA_SPACE_AES; +		break; +	case KEY_NONE: +	case KEY_WEP: +	case KEY_GEM: +		extra = 0; +		break; +	default: +		wl1271_warning("Unknown encryption type: %d", +			       wlvif->encryption_type); +		ret = -EINVAL; +		goto out; +	} + +	if (extra) { +		u8 *space = skb_push(skb, extra); +		memset(space, 0, extra); +	} + +	/* QoS header - BE */ +	if (wlvif->sta.qos) +		memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16)); +	/* mac80211 header */ +	hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr)); +	memset(hdr, 0, sizeof(hdr)); +	fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; +	if (wlvif->sta.qos) +		fc |= IEEE80211_STYPE_QOS_DATA; +	else +		fc |= IEEE80211_STYPE_DATA; +	if (wlvif->encryption_type != KEY_NONE) +		fc |= IEEE80211_FCTL_PROTECTED; + +	hdr->frame_control = cpu_to_le16(fc); +	memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); +	memcpy(hdr->addr2, vif->addr, ETH_ALEN); +	memset(hdr->addr3, 0xff, ETH_ALEN); + +	ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, +				      skb->data, skb->len, 0, +				      wlvif->basic_rate); +out: +	dev_kfree_skb(skb);  	return ret;  } @@ -1260,7 +1338,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif)  	/* FIXME: not sure what priority to use here */  	template.qos_ctrl = cpu_to_le16(0); -	return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, +	return wl1271_cmd_template_set(wl, wlvif->role_id, +				       CMD_TEMPL_QOS_NULL_DATA, &template,  				       sizeof(template), 0,  				       wlvif->basic_rate);  } @@ -1739,11 +1818,20 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)  		goto out;  	__clear_bit(role_id, wl->roc_map); + +	/* +	 * Rearm the tx watchdog when removing the last ROC. This prevents +	 * recoveries due to just finished ROCs - when Tx hasn't yet had +	 * a chance to get out. +	 */ +	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) +		wl12xx_rearm_tx_watchdog_locked(wl);  out:  	return ret;  }  int wl12xx_cmd_channel_switch(struct wl1271 *wl, +			      struct wl12xx_vif *wlvif,  			      struct ieee80211_channel_switch *ch_switch)  {  	struct wl12xx_cmd_channel_switch *cmd; @@ -1757,10 +1845,13 @@ int wl12xx_cmd_channel_switch(struct wl1271 *wl,  		goto out;  	} +	cmd->role_id = wlvif->role_id;  	cmd->channel = ch_switch->channel->hw_value;  	cmd->switch_time = ch_switch->count; -	cmd->tx_suspend = ch_switch->block_tx; -	cmd->flush = 0; /* this value is ignored by the FW */ +	cmd->stop_tx = ch_switch->block_tx; + +	/* FIXME: control from mac80211 in the future */ +	cmd->post_switch_tx_disable = 0;  /* Enable TX on the target channel */  	ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);  	if (ret < 0) {  | 
