diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/hif_usb.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 334 | 
1 files changed, 236 insertions, 98 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 2d10239ce82..2e3a33a5340 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -17,11 +17,9 @@  #include "htc.h"  /* identify firmware images */ -#define FIRMWARE_AR7010		"ar7010.fw" -#define FIRMWARE_AR7010_1_1	"ar7010_1_1.fw" -#define FIRMWARE_AR9271		"ar9271.fw" +#define FIRMWARE_AR7010_1_1     "htc_7010.fw" +#define FIRMWARE_AR9271         "htc_9271.fw" -MODULE_FIRMWARE(FIRMWARE_AR7010);  MODULE_FIRMWARE(FIRMWARE_AR7010_1_1);  MODULE_FIRMWARE(FIRMWARE_AR9271); @@ -80,7 +78,7 @@ static void hif_usb_regout_cb(struct urb *urb)  	if (cmd) {  		ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, -					  cmd->skb, 1); +					  cmd->skb, true);  		kfree(cmd);  	} @@ -126,6 +124,90 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,  	return ret;  } +static void hif_usb_mgmt_cb(struct urb *urb) +{ +	struct cmd_buf *cmd = (struct cmd_buf *)urb->context; +	struct hif_device_usb *hif_dev = cmd->hif_dev; +	bool txok = true; + +	if (!cmd || !cmd->skb || !cmd->hif_dev) +		return; + +	switch (urb->status) { +	case 0: +		break; +	case -ENOENT: +	case -ECONNRESET: +	case -ENODEV: +	case -ESHUTDOWN: +		txok = false; + +		/* +		 * If the URBs are being flushed, no need to complete +		 * this packet. +		 */ +		spin_lock(&hif_dev->tx.tx_lock); +		if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { +			spin_unlock(&hif_dev->tx.tx_lock); +			dev_kfree_skb_any(cmd->skb); +			kfree(cmd); +			return; +		} +		spin_unlock(&hif_dev->tx.tx_lock); + +		break; +	default: +		txok = false; +		break; +	} + +	skb_pull(cmd->skb, 4); +	ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, +				  cmd->skb, txok); +	kfree(cmd); +} + +static int hif_usb_send_mgmt(struct hif_device_usb *hif_dev, +			     struct sk_buff *skb) +{ +	struct urb *urb; +	struct cmd_buf *cmd; +	int ret = 0; +	__le16 *hdr; + +	urb = usb_alloc_urb(0, GFP_ATOMIC); +	if (urb == NULL) +		return -ENOMEM; + +	cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); +	if (cmd == NULL) { +		usb_free_urb(urb); +		return -ENOMEM; +	} + +	cmd->skb = skb; +	cmd->hif_dev = hif_dev; + +	hdr = (__le16 *) skb_push(skb, 4); +	*hdr++ = cpu_to_le16(skb->len - 4); +	*hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG); + +	usb_fill_bulk_urb(urb, hif_dev->udev, +			 usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE), +			 skb->data, skb->len, +			 hif_usb_mgmt_cb, cmd); + +	usb_anchor_urb(urb, &hif_dev->mgmt_submitted); +	ret = usb_submit_urb(urb, GFP_ATOMIC); +	if (ret) { +		usb_unanchor_urb(urb); +		kfree(cmd); +	} +	usb_free_urb(urb); + +	return ret; +} +  static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,  					 struct sk_buff_head *list)  { @@ -133,7 +215,22 @@ static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,  	while ((skb = __skb_dequeue(list)) != NULL) {  		dev_kfree_skb_any(skb); -		TX_STAT_INC(skb_dropped); +	} +} + +static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev, +					    struct sk_buff_head *queue, +					    bool txok) +{ +	struct sk_buff *skb; + +	while ((skb = __skb_dequeue(queue)) != NULL) { +		ath9k_htc_txcompletion_cb(hif_dev->htc_handle, +					  skb, txok); +		if (txok) +			TX_STAT_INC(skb_success); +		else +			TX_STAT_INC(skb_failed);  	}  } @@ -141,7 +238,7 @@ static void hif_usb_tx_cb(struct urb *urb)  {  	struct tx_buf *tx_buf = (struct tx_buf *) urb->context;  	struct hif_device_usb *hif_dev; -	struct sk_buff *skb; +	bool txok = true;  	if (!tx_buf || !tx_buf->hif_dev)  		return; @@ -155,10 +252,7 @@ static void hif_usb_tx_cb(struct urb *urb)  	case -ECONNRESET:  	case -ENODEV:  	case -ESHUTDOWN: -		/* -		 * The URB has been killed, free the SKBs. -		 */ -		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); +		txok = false;  		/*  		 * If the URBs are being flushed, no need to add this @@ -167,41 +261,19 @@ static void hif_usb_tx_cb(struct urb *urb)  		spin_lock(&hif_dev->tx.tx_lock);  		if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {  			spin_unlock(&hif_dev->tx.tx_lock); +			ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);  			return;  		}  		spin_unlock(&hif_dev->tx.tx_lock); -		/* -		 * In the stop() case, this URB has to be added to -		 * the free list. -		 */ -		goto add_free; +		break;  	default: +		txok = false;  		break;  	} -	/* -	 * Check if TX has been stopped, this is needed because -	 * this CB could have been invoked just after the TX lock -	 * was released in hif_stop() and kill_urb() hasn't been -	 * called yet. -	 */ -	spin_lock(&hif_dev->tx.tx_lock); -	if (hif_dev->tx.flags & HIF_USB_TX_STOP) { -		spin_unlock(&hif_dev->tx.tx_lock); -		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); -		goto add_free; -	} -	spin_unlock(&hif_dev->tx.tx_lock); - -	/* Complete the queued SKBs. */ -	while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) { -		ath9k_htc_txcompletion_cb(hif_dev->htc_handle, -					  skb, 1); -		TX_STAT_INC(skb_completed); -	} +	ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, txok); -add_free:  	/* Re-initialize the SKB queue */  	tx_buf->len = tx_buf->offset = 0;  	__skb_queue_head_init(&tx_buf->skb_queue); @@ -274,7 +346,7 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)  	ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC);  	if (ret) {  		tx_buf->len = tx_buf->offset = 0; -		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); +		ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, false);  		__skb_queue_head_init(&tx_buf->skb_queue);  		list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);  		hif_dev->tx.tx_buf_cnt++; @@ -286,10 +358,11 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)  	return ret;  } -static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb, -			   struct ath9k_htc_tx_ctl *tx_ctl) +static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb)  { +	struct ath9k_htc_tx_ctl *tx_ctl;  	unsigned long flags; +	int ret = 0;  	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); @@ -304,26 +377,36 @@ static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb,  		return -ENOMEM;  	} -	__skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb); -	hif_dev->tx.tx_skb_cnt++; +	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); -	/* Send normal frames immediately */ -	if (!tx_ctl || (tx_ctl && (tx_ctl->type == ATH9K_HTC_NORMAL))) -		__hif_usb_tx(hif_dev); +	tx_ctl = HTC_SKB_CB(skb); + +	/* Mgmt/Beacon frames don't use the TX buffer pool */ +	if ((tx_ctl->type == ATH9K_HTC_MGMT) || +	    (tx_ctl->type == ATH9K_HTC_BEACON)) { +		ret = hif_usb_send_mgmt(hif_dev, skb); +	} + +	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + +	if ((tx_ctl->type == ATH9K_HTC_NORMAL) || +	    (tx_ctl->type == ATH9K_HTC_AMPDU)) { +		__skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb); +		hif_dev->tx.tx_skb_cnt++; +	}  	/* Check if AMPDUs have to be sent immediately */ -	if (tx_ctl && (tx_ctl->type == ATH9K_HTC_AMPDU) && -	    (hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) && +	if ((hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) &&  	    (hif_dev->tx.tx_skb_cnt < 2)) {  		__hif_usb_tx(hif_dev);  	}  	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); -	return 0; +	return ret;  } -static void hif_usb_start(void *hif_handle, u8 pipe_id) +static void hif_usb_start(void *hif_handle)  {  	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;  	unsigned long flags; @@ -335,14 +418,14 @@ static void hif_usb_start(void *hif_handle, u8 pipe_id)  	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);  } -static void hif_usb_stop(void *hif_handle, u8 pipe_id) +static void hif_usb_stop(void *hif_handle)  {  	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;  	struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;  	unsigned long flags;  	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); -	ath9k_skb_queue_purge(hif_dev, &hif_dev->tx.tx_skb_queue); +	ath9k_skb_queue_complete(hif_dev, &hif_dev->tx.tx_skb_queue, false);  	hif_dev->tx.tx_skb_cnt = 0;  	hif_dev->tx.flags |= HIF_USB_TX_STOP;  	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); @@ -352,17 +435,18 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)  				 &hif_dev->tx.tx_pending, list) {  		usb_kill_urb(tx_buf->urb);  	} + +	usb_kill_anchored_urbs(&hif_dev->mgmt_submitted);  } -static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, -			struct ath9k_htc_tx_ctl *tx_ctl) +static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb)  {  	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;  	int ret = 0;  	switch (pipe_id) {  	case USB_WLAN_TX_PIPE: -		ret = hif_usb_send_tx(hif_dev, skb, tx_ctl); +		ret = hif_usb_send_tx(hif_dev, skb);  		break;  	case USB_REG_OUT_PIPE:  		ret = hif_usb_send_regout(hif_dev, skb); @@ -377,6 +461,40 @@ static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb,  	return ret;  } +static inline bool check_index(struct sk_buff *skb, u8 idx) +{ +	struct ath9k_htc_tx_ctl *tx_ctl; + +	tx_ctl = HTC_SKB_CB(skb); + +	if ((tx_ctl->type == ATH9K_HTC_AMPDU) && +	    (tx_ctl->sta_idx == idx)) +		return true; + +	return false; +} + +static void hif_usb_sta_drain(void *hif_handle, u8 idx) +{ +	struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; +	struct sk_buff *skb, *tmp; +	unsigned long flags; + +	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + +	skb_queue_walk_safe(&hif_dev->tx.tx_skb_queue, skb, tmp) { +		if (check_index(skb, idx)) { +			__skb_unlink(skb, &hif_dev->tx.tx_skb_queue); +			ath9k_htc_txcompletion_cb(hif_dev->htc_handle, +						  skb, false); +			hif_dev->tx.tx_skb_cnt--; +			TX_STAT_INC(skb_failed); +		} +	} + +	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} +  static struct ath9k_htc_hif hif_usb = {  	.transport = ATH9K_HIF_USB,  	.name = "ath9k_hif_usb", @@ -386,6 +504,7 @@ static struct ath9k_htc_hif hif_usb = {  	.start = hif_usb_start,  	.stop = hif_usb_stop, +	.sta_drain = hif_usb_sta_drain,  	.send = hif_usb_send,  }; @@ -567,6 +686,9 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)  	case -ESHUTDOWN:  		goto free;  	default: +		skb_reset_tail_pointer(skb); +		skb_trim(skb, 0); +  		goto resubmit;  	} @@ -591,23 +713,15 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)  						 USB_REG_IN_PIPE),  				 nskb->data, MAX_REG_IN_BUF_SIZE,  				 ath9k_hif_usb_reg_in_cb, nskb); - -		ret = usb_submit_urb(urb, GFP_ATOMIC); -		if (ret) { -			kfree_skb(nskb); -			urb->context = NULL; -		} - -		return;  	}  resubmit: -	skb_reset_tail_pointer(skb); -	skb_trim(skb, 0); - +	usb_anchor_urb(urb, &hif_dev->reg_in_submitted);  	ret = usb_submit_urb(urb, GFP_ATOMIC); -	if (ret) +	if (ret) { +		usb_unanchor_urb(urb);  		goto free; +	}  	return;  free: @@ -641,6 +755,8 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)  		kfree(tx_buf->buf);  		kfree(tx_buf);  	} + +	usb_kill_anchored_urbs(&hif_dev->mgmt_submitted);  }  static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) @@ -652,6 +768,7 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)  	INIT_LIST_HEAD(&hif_dev->tx.tx_pending);  	spin_lock_init(&hif_dev->tx.tx_lock);  	__skb_queue_head_init(&hif_dev->tx.tx_skb_queue); +	init_usb_anchor(&hif_dev->mgmt_submitted);  	for (i = 0; i < MAX_TX_URB_NUM; i++) {  		tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL); @@ -748,43 +865,67 @@ err_urb:  	return ret;  } -static void ath9k_hif_usb_dealloc_reg_in_urb(struct hif_device_usb *hif_dev) +static void ath9k_hif_usb_dealloc_reg_in_urbs(struct hif_device_usb *hif_dev)  { -	if (hif_dev->reg_in_urb) { -		usb_kill_urb(hif_dev->reg_in_urb); -		if (hif_dev->reg_in_urb->context) -			kfree_skb((void *)hif_dev->reg_in_urb->context); -		usb_free_urb(hif_dev->reg_in_urb); -		hif_dev->reg_in_urb = NULL; -	} +	usb_kill_anchored_urbs(&hif_dev->reg_in_submitted);  } -static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev) +static int ath9k_hif_usb_alloc_reg_in_urbs(struct hif_device_usb *hif_dev)  { -	struct sk_buff *skb; +	struct urb *urb = NULL; +	struct sk_buff *skb = NULL; +	int i, ret; -	hif_dev->reg_in_urb = usb_alloc_urb(0, GFP_KERNEL); -	if (hif_dev->reg_in_urb == NULL) -		return -ENOMEM; +	init_usb_anchor(&hif_dev->reg_in_submitted); -	skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL); -	if (!skb) -		goto err; +	for (i = 0; i < MAX_REG_IN_URB_NUM; i++) { -	usb_fill_bulk_urb(hif_dev->reg_in_urb, hif_dev->udev, -			 usb_rcvbulkpipe(hif_dev->udev, -					 USB_REG_IN_PIPE), -			 skb->data, MAX_REG_IN_BUF_SIZE, -			 ath9k_hif_usb_reg_in_cb, skb); +		/* Allocate URB */ +		urb = usb_alloc_urb(0, GFP_KERNEL); +		if (urb == NULL) { +			ret = -ENOMEM; +			goto err_urb; +		} -	if (usb_submit_urb(hif_dev->reg_in_urb, GFP_KERNEL) != 0) -		goto err; +		/* Allocate buffer */ +		skb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL); +		if (!skb) { +			ret = -ENOMEM; +			goto err_skb; +		} + +		usb_fill_bulk_urb(urb, hif_dev->udev, +				  usb_rcvbulkpipe(hif_dev->udev, +						  USB_REG_IN_PIPE), +				  skb->data, MAX_REG_IN_BUF_SIZE, +				  ath9k_hif_usb_reg_in_cb, skb); + +		/* Anchor URB */ +		usb_anchor_urb(urb, &hif_dev->reg_in_submitted); + +		/* Submit URB */ +		ret = usb_submit_urb(urb, GFP_KERNEL); +		if (ret) { +			usb_unanchor_urb(urb); +			goto err_submit; +		} + +		/* +		 * Drop reference count. +		 * This ensures that the URB is freed when killing them. +		 */ +		usb_free_urb(urb); +	}  	return 0; -err: -	ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); -	return -ENOMEM; +err_submit: +	kfree_skb(skb); +err_skb: +	usb_free_urb(urb); +err_urb: +	ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev); +	return ret;  }  static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev) @@ -801,7 +942,7 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)  		goto err_rx;  	/* Register Read */ -	if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0) +	if (ath9k_hif_usb_alloc_reg_in_urbs(hif_dev) < 0)  		goto err_reg;  	return 0; @@ -816,7 +957,7 @@ err:  static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev)  {  	usb_kill_anchored_urbs(&hif_dev->regout_submitted); -	ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); +	ath9k_hif_usb_dealloc_reg_in_urbs(hif_dev);  	ath9k_hif_usb_dealloc_tx_urbs(hif_dev);  	ath9k_hif_usb_dealloc_rx_urbs(hif_dev);  } @@ -1026,10 +1167,7 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface,  	/* Find out which firmware to load */  	if (IS_AR7010_DEVICE(id->driver_info)) -		if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x0202) -			hif_dev->fw_name = FIRMWARE_AR7010_1_1; -		else -			hif_dev->fw_name = FIRMWARE_AR7010; +		hif_dev->fw_name = FIRMWARE_AR7010_1_1;  	else  		hif_dev->fw_name = FIRMWARE_AR9271;  |