diff options
Diffstat (limited to 'drivers/net/wireless/p54/p54usb.c')
| -rw-r--r-- | drivers/net/wireless/p54/p54usb.c | 197 | 
1 files changed, 128 insertions, 69 deletions
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index f4d28c39aac..e1eac830e2f 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -117,21 +117,18 @@ static const struct {  	u32 intf;  	enum p54u_hw_type type;  	const char *fw; -	const char *fw_legacy;  	char hw[20];  } p54u_fwlist[__NUM_P54U_HWTYPES] = {  	{  		.type = P54U_NET2280,  		.intf = FW_LM86,  		.fw = "isl3886usb", -		.fw_legacy = "isl3890usb",  		.hw = "ISL3886 + net2280",  	},  	{  		.type = P54U_3887,  		.intf = FW_LM87,  		.fw = "isl3887usb", -		.fw_legacy = "isl3887usb_bare",  		.hw = "ISL3887",  	},  }; @@ -208,6 +205,16 @@ static void p54u_free_urbs(struct ieee80211_hw *dev)  	usb_kill_anchored_urbs(&priv->submitted);  } +static void p54u_stop(struct ieee80211_hw *dev) +{ +	/* +	 * TODO: figure out how to reliably stop the 3887 and net2280 so +	 * the hardware is still usable next time we want to start it. +	 * until then, we just stop listening to the hardware.. +	 */ +	p54u_free_urbs(dev); +} +  static int p54u_init_urbs(struct ieee80211_hw *dev)  {  	struct p54u_priv *priv = dev->priv; @@ -257,6 +264,16 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)  	return ret;  } +static int p54u_open(struct ieee80211_hw *dev) +{ +	/* +	 * TODO: Because we don't know how to reliably stop the 3887 and +	 * the isl3886+net2280, other than brutally cut off all +	 * communications. We have to reinitialize the urbs on every start. +	 */ +	return p54u_init_urbs(dev); +} +  static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)  {  	u32 chk = 0; @@ -836,70 +853,137 @@ fail:  	return err;  } -static int p54u_load_firmware(struct ieee80211_hw *dev) +static int p54_find_type(struct p54u_priv *priv)  { -	struct p54u_priv *priv = dev->priv; -	int err, i; - -	BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); +	int i;  	for (i = 0; i < __NUM_P54U_HWTYPES; i++)  		if (p54u_fwlist[i].type == priv->hw_type)  			break; -  	if (i == __NUM_P54U_HWTYPES)  		return -EOPNOTSUPP; -	err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev); -	if (err) { -		dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " -					  "(%d)!\n", p54u_fwlist[i].fw, err); +	return i; +} -		err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy, -				       &priv->udev->dev); -		if (err) -			return err; -	} +static int p54u_start_ops(struct p54u_priv *priv) +{ +	struct ieee80211_hw *dev = priv->common.hw; +	int ret; -	err = p54_parse_firmware(dev, priv->fw); -	if (err) -		goto out; +	ret = p54_parse_firmware(dev, priv->fw); +	if (ret) +		goto err_out; + +	ret = p54_find_type(priv); +	if (ret < 0) +		goto err_out; -	if (priv->common.fw_interface != p54u_fwlist[i].intf) { +	if (priv->common.fw_interface != p54u_fwlist[ret].intf) {  		dev_err(&priv->udev->dev, "wrong firmware, please get "  			"a firmware for \"%s\" and try again.\n", -			p54u_fwlist[i].hw); -		err = -EINVAL; +			p54u_fwlist[ret].hw); +		ret = -ENODEV; +		goto err_out;  	} -out: -	if (err) -		release_firmware(priv->fw); +	ret = priv->upload_fw(dev); +	if (ret) +		goto err_out; -	return err; +	ret = p54u_open(dev); +	if (ret) +		goto err_out; + +	ret = p54_read_eeprom(dev); +	if (ret) +		goto err_stop; + +	p54u_stop(dev); + +	ret = p54_register_common(dev, &priv->udev->dev); +	if (ret) +		goto err_stop; + +	return 0; + +err_stop: +	p54u_stop(dev); + +err_out: +	/* +	 * p54u_disconnect will do the rest of the +	 * cleanup +	 */ +	return ret;  } -static int p54u_open(struct ieee80211_hw *dev) +static void p54u_load_firmware_cb(const struct firmware *firmware, +				  void *context)  { -	struct p54u_priv *priv = dev->priv; +	struct p54u_priv *priv = context; +	struct usb_device *udev = priv->udev;  	int err; -	err = p54u_init_urbs(dev); -	if (err) { -		return err; +	complete(&priv->fw_wait_load); +	if (firmware) { +		priv->fw = firmware; +		err = p54u_start_ops(priv); +	} else { +		err = -ENOENT; +		dev_err(&udev->dev, "Firmware not found.\n");  	} -	priv->common.open = p54u_init_urbs; +	if (err) { +		struct device *parent = priv->udev->dev.parent; -	return 0; +		dev_err(&udev->dev, "failed to initialize device (%d)\n", err); + +		if (parent) +			device_lock(parent); + +		device_release_driver(&udev->dev); +		/* +		 * At this point p54u_disconnect has already freed +		 * the "priv" context. Do not use it anymore! +		 */ +		priv = NULL; + +		if (parent) +			device_unlock(parent); +	} + +	usb_put_dev(udev);  } -static void p54u_stop(struct ieee80211_hw *dev) +static int p54u_load_firmware(struct ieee80211_hw *dev, +			      struct usb_interface *intf)  { -	/* TODO: figure out how to reliably stop the 3887 and net2280 so -	   the hardware is still usable next time we want to start it. -	   until then, we just stop listening to the hardware.. */ -	p54u_free_urbs(dev); +	struct usb_device *udev = interface_to_usbdev(intf); +	struct p54u_priv *priv = dev->priv; +	struct device *device = &udev->dev; +	int err, i; + +	BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); + +	init_completion(&priv->fw_wait_load); +	i = p54_find_type(priv); +	if (i < 0) +		return i; + +	dev_info(&priv->udev->dev, "Loading firmware file %s\n", +	       p54u_fwlist[i].fw); + +	usb_get_dev(udev); +	err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, +				      device, GFP_KERNEL, priv, +				      p54u_load_firmware_cb); +	if (err) { +		dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " +					  "(%d)!\n", p54u_fwlist[i].fw, err); +	} + +	return err;  }  static int __devinit p54u_probe(struct usb_interface *intf, @@ -969,33 +1053,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,  		priv->common.tx = p54u_tx_net2280;  		priv->upload_fw = p54u_upload_firmware_net2280;  	} -	err = p54u_load_firmware(dev); -	if (err) -		goto err_free_dev; - -	err = priv->upload_fw(dev); -	if (err) -		goto err_free_fw; - -	p54u_open(dev); -	err = p54_read_eeprom(dev); -	p54u_stop(dev); -	if (err) -		goto err_free_fw; - -	err = p54_register_common(dev, &udev->dev); -	if (err) -		goto err_free_fw; - -	return 0; - -err_free_fw: -	release_firmware(priv->fw); - -err_free_dev: -	p54_free_common(dev); -	usb_set_intfdata(intf, NULL); -	usb_put_dev(udev); +	err = p54u_load_firmware(dev, intf);  	return err;  } @@ -1007,9 +1065,10 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)  	if (!dev)  		return; +	priv = dev->priv; +	wait_for_completion(&priv->fw_wait_load);  	p54_unregister_common(dev); -	priv = dev->priv;  	usb_put_dev(interface_to_usbdev(intf));  	release_firmware(priv->fw);  	p54_free_common(dev); @@ -1072,7 +1131,7 @@ static struct usb_driver p54u_driver = {  	.name	= "p54usb",  	.id_table = p54u_table,  	.probe = p54u_probe, -	.disconnect = p54u_disconnect, +	.disconnect = __devexit_p(p54u_disconnect),  	.pre_reset = p54u_pre_reset,  	.post_reset = p54u_post_reset,  #ifdef CONFIG_PM  |