diff options
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/rx.c')
| -rw-r--r-- | drivers/net/wireless/iwmc3200wifi/rx.c | 76 | 
1 files changed, 54 insertions, 22 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 3257d4fad83..ad539877924 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -343,15 +343,17 @@ static void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node)  static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id)  {  	u8 id_hash = IWM_RX_ID_GET_HASH(id); -	struct list_head *packet_list; -	struct iwm_rx_packet *packet, *next; - -	packet_list = &iwm->rx_packets[id_hash]; +	struct iwm_rx_packet *packet; -	list_for_each_entry_safe(packet, next, packet_list, node) -		if (packet->id == id) +	spin_lock(&iwm->packet_lock[id_hash]); +	list_for_each_entry(packet, &iwm->rx_packets[id_hash], node) +		if (packet->id == id) { +			list_del(&packet->node); +			spin_unlock(&iwm->packet_lock[id_hash]);  			return packet; +		} +	spin_unlock(&iwm->packet_lock[id_hash]);  	return NULL;  } @@ -389,18 +391,22 @@ void iwm_rx_free(struct iwm_priv *iwm)  	struct iwm_rx_packet *packet, *np;  	int i; +	spin_lock(&iwm->ticket_lock);  	list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) {  		list_del(&ticket->node);  		iwm_rx_ticket_node_free(ticket);  	} +	spin_unlock(&iwm->ticket_lock);  	for (i = 0; i < IWM_RX_ID_HASH; i++) { +		spin_lock(&iwm->packet_lock[i]);  		list_for_each_entry_safe(packet, np, &iwm->rx_packets[i],  					 node) {  			list_del(&packet->node);  			kfree_skb(packet->skb);  			kfree(packet);  		} +		spin_unlock(&iwm->packet_lock[i]);  	}  } @@ -428,7 +434,9 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,  				   ticket->action ==  IWM_RX_TICKET_RELEASE ?  				   "RELEASE" : "DROP",  				   ticket->id); +			spin_lock(&iwm->ticket_lock);  			list_add_tail(&ticket_node->node, &iwm->rx_tickets); +			spin_unlock(&iwm->ticket_lock);  			/*  			 * We received an Rx ticket, most likely there's @@ -461,6 +469,7 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,  	struct iwm_rx_packet *packet;  	u16 id, buf_offset;  	u32 packet_size; +	u8 id_hash;  	IWM_DBG_RX(iwm, DBG, "\n"); @@ -478,7 +487,10 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,  	if (IS_ERR(packet))  		return PTR_ERR(packet); -	list_add_tail(&packet->node, &iwm->rx_packets[IWM_RX_ID_GET_HASH(id)]); +	id_hash = IWM_RX_ID_GET_HASH(id); +	spin_lock(&iwm->packet_lock[id_hash]); +	list_add_tail(&packet->node, &iwm->rx_packets[id_hash]); +	spin_unlock(&iwm->packet_lock[id_hash]);  	/* We might (unlikely) have received the packet _after_ the ticket */  	queue_work(iwm->rx_wq, &iwm->rx_worker); @@ -519,6 +531,8 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,  				   unsigned long buf_size,  				   struct iwm_wifi_cmd *cmd)  { +	struct wiphy *wiphy = iwm_to_wiphy(iwm); +	struct ieee80211_channel *chan;  	struct iwm_umac_notif_assoc_complete *complete =  		(struct iwm_umac_notif_assoc_complete *)buf; @@ -527,6 +541,18 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,  	switch (le32_to_cpu(complete->status)) {  	case UMAC_ASSOC_COMPLETE_SUCCESS: +		chan = ieee80211_get_channel(wiphy, +			ieee80211_channel_to_frequency(complete->channel)); +		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { +			/* Associated to a unallowed channel, disassociate. */ +			__iwm_invalidate_mlme_profile(iwm); +			IWM_WARN(iwm, "Couldn't associate with %pM due to " +				 "channel %d is disabled. Check your local " +				 "regulatory setting.\n", +				 complete->bssid, complete->channel); +			goto failure; +		} +  		set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);  		memcpy(iwm->bssid, complete->bssid, ETH_ALEN);  		iwm->channel = complete->channel; @@ -563,6 +589,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,  					GFP_KERNEL);  		break;  	case UMAC_ASSOC_COMPLETE_FAILURE: + failure:  		clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);  		memset(iwm->bssid, 0, ETH_ALEN);  		iwm->channel = 0; @@ -757,7 +784,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,  			(struct iwm_umac_notif_bss_info *)buf;  	struct ieee80211_channel *channel;  	struct ieee80211_supported_band *band; -	struct iwm_bss_info *bss, *next; +	struct iwm_bss_info *bss;  	s32 signal;  	int freq;  	u16 frame_len = le16_to_cpu(umac_bss->frame_len); @@ -776,7 +803,7 @@ static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,  	IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi);  	IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len); -	list_for_each_entry_safe(bss, next, &iwm->bss_list, node) +	list_for_each_entry(bss, &iwm->bss_list, node)  		if (bss->bss->table_idx == umac_bss->table_idx)  			break; @@ -843,16 +870,15 @@ static int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf,  	int i;  	for (i = 0; i < le32_to_cpu(bss_rm->count); i++) { -		table_idx = (le16_to_cpu(bss_rm->entries[i]) -			     & IWM_BSS_REMOVE_INDEX_MSK); +		table_idx = le16_to_cpu(bss_rm->entries[i]) & +			    IWM_BSS_REMOVE_INDEX_MSK;  		list_for_each_entry_safe(bss, next, &iwm->bss_list, node)  			if (bss->bss->table_idx == cpu_to_le16(table_idx)) {  				struct ieee80211_mgmt *mgmt;  				mgmt = (struct ieee80211_mgmt *)  					(bss->bss->frame_buf); -				IWM_DBG_MLME(iwm, ERR, -					     "BSS removed: %pM\n", +				IWM_DBG_MLME(iwm, ERR, "BSS removed: %pM\n",  					     mgmt->bssid);  				list_del(&bss->node);  				kfree(bss->bss); @@ -1224,18 +1250,24 @@ static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,  	u8 source, cmd_id;  	u16 seq_num;  	u32 count; -	u8 resp;  	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;  	cmd_id = wifi_hdr->sw_hdr.cmd.cmd; -  	source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);  	if (source >= IWM_SRC_NUM) {  		IWM_CRIT(iwm, "invalid source %d\n", source);  		return -EINVAL;  	} -	count = (GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT)); +	if (cmd_id == REPLY_RX_MPDU_CMD) +		trace_iwm_rx_packet(iwm, buf, buf_size); +	else if ((cmd_id == UMAC_NOTIFY_OPCODE_RX_TICKET) && +		 (source == UMAC_HDI_IN_SOURCE_FW)) +		trace_iwm_rx_ticket(iwm, buf, buf_size); +	else +		trace_iwm_rx_wifi_cmd(iwm, wifi_hdr); + +	count = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT);  	count += sizeof(struct iwm_umac_wifi_in_hdr) -  		 sizeof(struct iwm_dev_cmd_hdr);  	if (count > buf_size) { @@ -1243,8 +1275,6 @@ static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,  		return -EINVAL;  	} -	resp = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_STATUS); -  	seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);  	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n", @@ -1317,8 +1347,9 @@ static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,  {  	u8 seq_num;  	struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf; -	struct iwm_nonwifi_cmd *cmd, *next; +	struct iwm_nonwifi_cmd *cmd; +	trace_iwm_rx_nonwifi_cmd(iwm, buf, buf_size);  	seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);  	/* @@ -1329,7 +1360,7 @@ static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,  	 * That means we only support synchronised non wifi command response  	 * schemes.  	 */ -	list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending) +	list_for_each_entry(cmd, &iwm->nonwifi_pending_cmd, pending)  		if (cmd->seq_num == seq_num) {  			cmd->resp_received = 1;  			cmd->buf.len = buf_size; @@ -1648,6 +1679,7 @@ void iwm_rx_worker(struct work_struct *work)  	 * We stop whenever a ticket is missing its packet, as we're  	 * supposed to send the packets in order.  	 */ +	spin_lock(&iwm->ticket_lock);  	list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {  		struct iwm_rx_packet *packet =  			iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id)); @@ -1656,12 +1688,12 @@ void iwm_rx_worker(struct work_struct *work)  			IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d "  				   "to be handled first\n",  				   le16_to_cpu(ticket->ticket->id)); -			return; +			break;  		}  		list_del(&ticket->node); -		list_del(&packet->node);  		iwm_rx_process_packet(iwm, packet, ticket);  	} +	spin_unlock(&iwm->ticket_lock);  }  |