diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/usb-tx.c')
| -rw-r--r-- | drivers/net/wimax/i2400m/usb-tx.c | 61 | 
1 files changed, 54 insertions, 7 deletions
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c index dfd893356f4..c65b9979f87 100644 --- a/drivers/net/wimax/i2400m/usb-tx.c +++ b/drivers/net/wimax/i2400m/usb-tx.c @@ -101,11 +101,11 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,  		dev_err(dev, "TX: can't get autopm: %d\n", result);  		do_autopm = 0;  	} -	epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); +	epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);  	usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);  retry:  	result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe, -			      tx_msg, tx_msg_size, &sent_size, HZ); +			      tx_msg, tx_msg_size, &sent_size, 200);  	usb_mark_last_busy(i2400mu->usb_dev);  	switch (result) {  	case 0: @@ -115,6 +115,28 @@ retry:  			result = -EIO;  		}  		break; +	case -EPIPE: +		/* +		 * Stall -- maybe the device is choking with our +		 * requests. Clear it and give it some time. If they +		 * happen to often, it might be another symptom, so we +		 * reset. +		 * +		 * No error handling for usb_clear_halt(0; if it +		 * works, the retry works; if it fails, this switch +		 * does the error handling for us. +		 */ +		if (edc_inc(&i2400mu->urb_edc, +			    10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { +			dev_err(dev, "BM-CMD: too many stalls in " +				"URB; resetting device\n"); +			usb_queue_reset_device(i2400mu->usb_iface); +			/* fallthrough */ +		} else { +			usb_clear_halt(i2400mu->usb_dev, usb_pipe); +			msleep(10);	/* give the device some time */ +			goto retry; +		}  	case -EINVAL:			/* while removing driver */  	case -ENODEV:			/* dev disconnect ... */  	case -ENOENT:			/* just ignore it */ @@ -161,9 +183,15 @@ int i2400mu_txd(void *_i2400mu)  	struct device *dev = &i2400mu->usb_iface->dev;  	struct i2400m_msg_hdr *tx_msg;  	size_t tx_msg_size; +	unsigned long flags;  	d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); +	spin_lock_irqsave(&i2400m->tx_lock, flags); +	BUG_ON(i2400mu->tx_kthread != NULL); +	i2400mu->tx_kthread = current; +	spin_unlock_irqrestore(&i2400m->tx_lock, flags); +  	while (1) {  		d_printf(2, dev, "TX: waiting for messages\n");  		tx_msg = NULL; @@ -183,6 +211,11 @@ int i2400mu_txd(void *_i2400mu)  		if (result < 0)  			break;  	} + +	spin_lock_irqsave(&i2400m->tx_lock, flags); +	i2400mu->tx_kthread = NULL; +	spin_unlock_irqrestore(&i2400m->tx_lock, flags); +  	d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);  	return result;  } @@ -213,11 +246,13 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)  	struct i2400m *i2400m = &i2400mu->i2400m;  	struct device *dev = &i2400mu->usb_iface->dev;  	struct wimax_dev *wimax_dev = &i2400m->wimax_dev; +	struct task_struct *kthread; -	i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", -					  wimax_dev->name); -	if (IS_ERR(i2400mu->tx_kthread)) { -		result = PTR_ERR(i2400mu->tx_kthread); +	kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", +			      wimax_dev->name); +	/* the kthread function sets i2400mu->tx_thread */ +	if (IS_ERR(kthread)) { +		result = PTR_ERR(kthread);  		dev_err(dev, "TX: cannot start thread: %d\n", result);  	}  	return result; @@ -225,5 +260,17 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)  void i2400mu_tx_release(struct i2400mu *i2400mu)  { -	kthread_stop(i2400mu->tx_kthread); +	unsigned long flags; +	struct i2400m *i2400m = &i2400mu->i2400m; +	struct device *dev = i2400m_dev(i2400m); +	struct task_struct *kthread; + +	spin_lock_irqsave(&i2400m->tx_lock, flags); +	kthread = i2400mu->tx_kthread; +	i2400mu->tx_kthread = NULL; +	spin_unlock_irqrestore(&i2400m->tx_lock, flags); +	if (kthread) +		kthread_stop(kthread); +	else +		d_printf(1, dev, "TX: kthread had already exited\n");  }  |