p54pci: convert driver to use asynchronous firmware loading

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Sat Sep 29 10:59:14 EDT 2012


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=95a96e08967fed6e64e53b2d6a5e153a4d605e11
Commit:     95a96e08967fed6e64e53b2d6a5e153a4d605e11
Parent:     d11d354b7b02aac09a20a8157bf990670f169f6f
Author:     Larry Finger <Larry.Finger at lwfinger.net>
AuthorDate: Tue Jul 24 21:16:42 2012 +0200
Committer:  John W. Linville <linville at tuxdriver.com>
CommitDate: Mon Aug 6 14:56:33 2012 -0400

    p54pci: convert driver to use asynchronous firmware loading
    
    Drivers that load firmware from their probe routine have problems with the
    latest versions of udev as they get timeouts while waiting for user
    space to start. The problem is fixed by using request_firmware_nowait()
    and delaying the start of mac80211 until the firmware is loaded.
    
    To prevent the possibility of the driver being unloaded while the firmware
    loading callback is still active, a completion queue entry is used.
    
    Signed-off-by: Larry Finger <Larry.Finger at lwfinger.net>
    Signed-off-by: Christian Lamparter <chunkeey at googlemail.com>
    Signed-off-by: John W. Linville <linville at tuxdriver.com>
---
 drivers/net/wireless/p54/p54pci.c |   88 ++++++++++++++++++++++++++-----------
 drivers/net/wireless/p54/p54pci.h |    1 +
 2 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index 89318ad..b439079 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -488,6 +488,58 @@ static int p54p_open(struct ieee80211_hw *dev)
 	return 0;
 }
 
+static void p54p_firmware_step2(const struct firmware *fw,
+				void *context)
+{
+	struct p54p_priv *priv = context;
+	struct ieee80211_hw *dev = priv->common.hw;
+	struct pci_dev *pdev = priv->pdev;
+	int err;
+
+	if (!fw) {
+		dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n");
+		err = -ENOENT;
+		goto out;
+	}
+
+	priv->firmware = fw;
+
+	err = p54p_open(dev);
+	if (err)
+		goto out;
+	err = p54_read_eeprom(dev);
+	p54p_stop(dev);
+	if (err)
+		goto out;
+
+	err = p54_register_common(dev, &pdev->dev);
+	if (err)
+		goto out;
+
+out:
+
+	complete(&priv->fw_loaded);
+
+	if (err) {
+		struct device *parent = pdev->dev.parent;
+
+		if (parent)
+			device_lock(parent);
+
+		/*
+		 * This will indirectly result in a call to p54p_remove.
+		 * Hence, we don't need to bother with freeing any
+		 * allocated ressources at all.
+		 */
+		device_release_driver(&pdev->dev);
+
+		if (parent)
+			device_unlock(parent);
+	}
+
+	pci_dev_put(pdev);
+}
+
 static int __devinit p54p_probe(struct pci_dev *pdev,
 				const struct pci_device_id *id)
 {
@@ -496,6 +548,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
 	unsigned long mem_addr, mem_len;
 	int err;
 
+	pci_dev_get(pdev);
 	err = pci_enable_device(pdev);
 	if (err) {
 		dev_err(&pdev->dev, "Cannot enable new PCI device\n");
@@ -537,6 +590,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
 	priv = dev->priv;
 	priv->pdev = pdev;
 
+	init_completion(&priv->fw_loaded);
 	SET_IEEE80211_DEV(dev, &pdev->dev);
 	pci_set_drvdata(pdev, dev);
 
@@ -561,32 +615,12 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
 	spin_lock_init(&priv->lock);
 	tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev);
 
-	err = request_firmware(&priv->firmware, "isl3886pci",
-			       &priv->pdev->dev);
-	if (err) {
-		dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n");
-		err = request_firmware(&priv->firmware, "isl3886",
-				       &priv->pdev->dev);
-		if (err)
-			goto err_free_common;
-	}
-
-	err = p54p_open(dev);
-	if (err)
-		goto err_free_common;
-	err = p54_read_eeprom(dev);
-	p54p_stop(dev);
-	if (err)
-		goto err_free_common;
-
-	err = p54_register_common(dev, &pdev->dev);
-	if (err)
-		goto err_free_common;
-
-	return 0;
+	err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci",
+				      &priv->pdev->dev, GFP_KERNEL,
+				      priv, p54p_firmware_step2);
+	if (!err)
+		return 0;
 
- err_free_common:
-	release_firmware(priv->firmware);
 	pci_free_consistent(pdev, sizeof(*priv->ring_control),
 			    priv->ring_control, priv->ring_control_dma);
 
@@ -601,6 +635,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
 	pci_release_regions(pdev);
  err_disable_dev:
 	pci_disable_device(pdev);
+	pci_dev_put(pdev);
 	return err;
 }
 
@@ -612,8 +647,9 @@ static void __devexit p54p_remove(struct pci_dev *pdev)
 	if (!dev)
 		return;
 
-	p54_unregister_common(dev);
 	priv = dev->priv;
+	wait_for_completion(&priv->fw_loaded);
+	p54_unregister_common(dev);
 	release_firmware(priv->firmware);
 	pci_free_consistent(pdev, sizeof(*priv->ring_control),
 			    priv->ring_control, priv->ring_control_dma);
diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h
index 7aa509f..68405c1 100644
--- a/drivers/net/wireless/p54/p54pci.h
+++ b/drivers/net/wireless/p54/p54pci.h
@@ -105,6 +105,7 @@ struct p54p_priv {
 	struct sk_buff *tx_buf_data[32];
 	struct sk_buff *tx_buf_mgmt[4];
 	struct completion boot_comp;
+	struct completion fw_loaded;
 };
 
 #endif /* P54USB_H */



More information about the linux-mtd-cvs mailing list