diff options
| author | Patrick McHardy <kaber@trash.net> | 2010-05-10 18:39:28 +0200 | 
|---|---|---|
| committer | Patrick McHardy <kaber@trash.net> | 2010-05-10 18:39:28 +0200 | 
| commit | 1e4b1057121bc756b91758a434b504d2010f6088 (patch) | |
| tree | b016cf2c728289c7e36d9e4e488f30ab0bd0ae6e /drivers/net/wireless/ath/ath9k/hif_usb.c | |
| parent | 3b254c54ec46eb022cb26ee6ab37fae23f5f7d6a (diff) | |
| parent | 3ee943728fff536edaf8f59faa58aaa1aa7366e3 (diff) | |
| download | olio-linux-3.10-1e4b1057121bc756b91758a434b504d2010f6088.tar.xz olio-linux-3.10-1e4b1057121bc756b91758a434b504d2010f6088.zip  | |
Merge branch 'master' of /repos/git/net-next-2.6
Conflicts:
	net/bridge/br_device.c
	net/bridge/br_forward.c
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/hif_usb.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 142 | 
1 files changed, 83 insertions, 59 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index fe994e22989..74872ca76f9 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -93,14 +93,24 @@ static int hif_usb_send_regout(struct hif_device_usb *hif_dev,  	return ret;  } +static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev, +					 struct sk_buff_head *list) +{ +	struct sk_buff *skb; + +	while ((skb = __skb_dequeue(list)) != NULL) { +		dev_kfree_skb_any(skb); +		TX_STAT_INC(skb_dropped); +	} +} +  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 = tx_buf->hif_dev;  	struct sk_buff *skb; -	bool drop, flush; -	if (!hif_dev) +	if (!hif_dev || !tx_buf)  		return;  	switch (urb->status) { @@ -108,52 +118,47 @@ static void hif_usb_tx_cb(struct urb *urb)  		break;  	case -ENOENT:  	case -ECONNRESET: -		break;  	case -ENODEV:  	case -ESHUTDOWN: +		/* +		 * The URB has been killed, free the SKBs +		 * and return. +		 */ +		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);  		return;  	default:  		break;  	} -	if (tx_buf) { -		spin_lock(&hif_dev->tx.tx_lock); -		drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP); -		flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH); +	/* Check if TX has been stopped */ +	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); -		while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) { -			if (!drop && !flush) { -				ath9k_htc_txcompletion_cb(hif_dev->htc_handle, -							  skb, 1); -				TX_STAT_INC(skb_completed); -			} else { -				dev_kfree_skb_any(skb); -			} -		} - -		if (flush) -			return; - -		tx_buf->len = tx_buf->offset = 0; -		__skb_queue_head_init(&tx_buf->skb_queue); - -		spin_lock(&hif_dev->tx.tx_lock); -		list_del(&tx_buf->list); -		list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); -		hif_dev->tx.tx_buf_cnt++; -		if (!drop) -			__hif_usb_tx(hif_dev); /* Check for pending SKBs */ -		TX_STAT_INC(buf_completed); -		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);  	} -} -static inline void ath9k_skb_queue_purge(struct sk_buff_head *list) -{ -	struct sk_buff *skb; -	while ((skb = __skb_dequeue(list)) != NULL) -		dev_kfree_skb_any(skb); +add_free: +	/* Re-initialize the SKB queue */ +	tx_buf->len = tx_buf->offset = 0; +	__skb_queue_head_init(&tx_buf->skb_queue); + +	/* Add this TX buffer to the free list */ +	spin_lock(&hif_dev->tx.tx_lock); +	list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); +	hif_dev->tx.tx_buf_cnt++; +	if (!(hif_dev->tx.flags & HIF_USB_TX_STOP)) +		__hif_usb_tx(hif_dev); /* Check for pending SKBs */ +	TX_STAT_INC(buf_completed); +	spin_unlock(&hif_dev->tx.tx_lock);  }  /* TX lock has to be taken */ @@ -173,8 +178,7 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)  		return 0;  	tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list); -	list_del(&tx_buf->list); -	list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending); +	list_move_tail(&tx_buf->list, &hif_dev->tx.tx_pending);  	hif_dev->tx.tx_buf_cnt--;  	tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM); @@ -214,7 +218,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(&tx_buf->skb_queue); +		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);  		__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++; @@ -281,7 +285,7 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)  	unsigned long flags;  	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); -	ath9k_skb_queue_purge(&hif_dev->tx.tx_skb_queue); +	ath9k_skb_queue_purge(hif_dev, &hif_dev->tx.tx_skb_queue);  	hif_dev->tx.tx_skb_cnt = 0;  	hif_dev->tx.flags |= HIF_USB_TX_STOP;  	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); @@ -506,9 +510,18 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)  	if (likely(urb->actual_length != 0)) {  		skb_put(skb, urb->actual_length); +		/* Process the command first */ +		ath9k_htc_rx_msg(hif_dev->htc_handle, skb, +				 skb->len, USB_REG_IN_PIPE); + +  		nskb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); -		if (!nskb) -			goto resubmit; +		if (!nskb) { +			dev_err(&hif_dev->udev->dev, +				"ath9k_htc: REG_IN memory allocation failure\n"); +			urb->context = NULL; +			return; +		}  		usb_fill_int_urb(urb, hif_dev->udev,  				 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), @@ -518,12 +531,9 @@ static void ath9k_hif_usb_reg_in_cb(struct urb *urb)  		ret = usb_submit_urb(urb, GFP_ATOMIC);  		if (ret) {  			kfree_skb(nskb); -			goto free; +			urb->context = NULL;  		} -		ath9k_htc_rx_msg(hif_dev->htc_handle, skb, -				 skb->len, USB_REG_IN_PIPE); -  		return;  	} @@ -543,20 +553,17 @@ free:  static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)  { -	unsigned long flags;  	struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; -	list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) { +	list_for_each_entry_safe(tx_buf, tx_buf_tmp, +				 &hif_dev->tx.tx_buf, list) { +		usb_kill_urb(tx_buf->urb);  		list_del(&tx_buf->list);  		usb_free_urb(tx_buf->urb);  		kfree(tx_buf->buf);  		kfree(tx_buf);  	} -	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); -	hif_dev->tx.flags |= HIF_USB_TX_FLUSH; -	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); -  	list_for_each_entry_safe(tx_buf, tx_buf_tmp,  				 &hif_dev->tx.tx_pending, list) {  		usb_kill_urb(tx_buf->urb); @@ -565,10 +572,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)  		kfree(tx_buf->buf);  		kfree(tx_buf);  	} - -	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); -	hif_dev->tx.flags &= ~HIF_USB_TX_FLUSH; -	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);  }  static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) @@ -892,6 +895,26 @@ err_alloc:  	return ret;  } +static void ath9k_hif_usb_reboot(struct usb_device *udev) +{ +	u32 reboot_cmd = 0xffffffff; +	void *buf; +	int ret; + +	buf = kmalloc(4, GFP_KERNEL); +	if (!buf) +		return; + +	memcpy(buf, &reboot_cmd, 4); + +	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE), +			   buf, 4, NULL, HZ); +	if (ret) +		dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n"); + +	kfree(buf); +} +  static void ath9k_hif_usb_disconnect(struct usb_interface *interface)  {  	struct usb_device *udev = interface_to_usbdev(interface); @@ -899,14 +922,15 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface)  		(struct hif_device_usb *) usb_get_intfdata(interface);  	if (hif_dev) { -		ath9k_htc_hw_deinit(hif_dev->htc_handle, true); +		ath9k_htc_hw_deinit(hif_dev->htc_handle, +		    (udev->state == USB_STATE_NOTATTACHED) ? true : false);  		ath9k_htc_hw_free(hif_dev->htc_handle);  		ath9k_hif_usb_dev_deinit(hif_dev);  		usb_set_intfdata(interface, NULL);  	}  	if (hif_dev->flags & HIF_USB_START) -		usb_reset_device(udev); +		ath9k_hif_usb_reboot(udev);  	kfree(hif_dev);  	dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n");  |