Search Linux Wireless

Re: [RFC/RFT] p54usb: Convert to asynchronous firmware loading

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey Larry!

On Thursday, February 09, 2012 04:29:47 AM Larry Finger wrote:
> 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@xxxxxxxxxxxx>
> ---
> 
> I will be submitting trial patches for p54pci and p54spi, but I do not have
> the hardware and will not be able to test.
> 
> Larry
That's great. For p54spi we better ask Max <jcmvbkbc@xxxxxxxxx> and
maybe Michael <m@xxxxxxx>.

> ---
> 
>  p54usb.c |  153 ++++++++++++++++++++++++++++++++++++++++++++++-----------------
>  p54usb.h |    3 +
>  2 files changed, 115 insertions(+), 41 deletions(-)
> ---
> 
> Index: wireless-testing-new/drivers/net/wireless/p54/p54usb.c
> ===================================================================
> --- wireless-testing-new.orig/drivers/net/wireless/p54/p54usb.c
> +++ wireless-testing-new/drivers/net/wireless/p54/p54usb.c  
> [...]
> -out:
> +static void p54u_load_firmware_cb(const struct firmware *firmware,
> +				  void *context)
> +{
> +	struct usb_interface *intf = context;
> +	struct ieee80211_hw *dev = usb_get_intfdata(intf);
> +	struct p54u_priv *priv = dev->priv;
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	struct device *device = &udev->dev;
> +	int i, err;
> +
> +	if (!firmware) {
> +		i = p54_find_type(priv);
> +		if (i < 0)
> +			return;
> +		/* Primary firmware not found - try for legacy variety */
> +		dev_info(&priv->udev->dev, "Loading firmware file %s\n",
> +			 p54u_fwlist[i].fw_legacy);
> +		err = request_firmware_nowait(THIS_MODULE, 1,
> +					      p54u_fwlist[i].fw_legacy,
> +					      device, GFP_KERNEL, intf,
> +					      p54u_load_firmware_cb2);
> +		if (err)
> +			return;
> +	}
> +	complete(&priv->fw_loaded);
> +	priv->fw = firmware;
> +	p54_start_ops(intf);
> +}

are you sure if the "if (err)" is correct? AFAIK request_firmware_nowait
returns 0 when the request is queued so we won't "go through the return"
and the priv->fw will be NULL {which seems kind of odd?}

I think we could do something like:
	err = request_firmware_nowait(THIS_MODULE, 1,
					      p54u_fwlist[i].fw_legacy,
					      device, GFP_KERNEL, intf,
					      p54u_load_firmware_cb2);	

	if (WARN_ON_ONCE(err, "failed to request legacy firmware")) {
		p54u_firmware_load_failed(priv);
	}
	
	return;
}

and 

void p54u_firmware_load_failed(struct p54u_priv *priv)
{
        struct device *parent = priv->udev->dev.parent;
        struct usb_device *udev;

        /*
         * Store a copy of the usb_device pointer locally.
         * This is because device_release_driver initiates
         * p54u_disconnect, which in turn frees our
         * driver context (priv).
         */
        udev = priv->udev;

        complete(&priv->fw_load_wait);

        /* unbind anything failed */
        if (parent)
                device_lock(parent);

        device_release_driver(&udev->dev);
        if (parent)
                device_unlock(parent);

        usb_put_dev(udev);
}

Of course, now we could also modify p54u_load_firmware_cb2 to
call p54u_firmware_load_failed as well.

> +static void p54u_load_firmware_cb2(const struct firmware *firmware,
> +                                  void *context)
> +{
> +       struct usb_interface *intf = context;
> +       struct ieee80211_hw *dev = usb_get_intfdata(intf);
> +       struct p54u_priv *priv = dev->priv;
> +
> +       complete(&priv->fw_loaded);
> +       if (!firmware) {
> +               dev_err(&priv->udev->dev, "Firmware not found\n");

just add:
				p54u_firmware_load_failed(priv);


> +               return;
>         }
> +       priv->fw = firmware;
> +       p54_start_ops(intf);
> +}


> [...]

> +
> +static int p54u_load_firmware(struct ieee80211_hw *dev,
> +			      struct usb_interface *intf)
> +{
> +	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);
Yeah, I think I was up all night when I wrote that. The BUILD_BUG_ON is a bit
is completey superfluous.

> +
> +	init_completion(&priv->fw_loaded);
> +	i = p54_find_type(priv);
> +	if (i < 0)
> +		return i;
> +
> +	dev_info(&priv->udev->dev, "Loading firmware file %s\n",
> +	       p54u_fwlist[i].fw);
> +	err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw,
> +				      device, GFP_KERNEL, intf,
> +				      p54u_load_firmware_cb);
>  	if (err)
> -		release_firmware(priv->fw);
> +		dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
> +					  "(%d)!\n", p54u_fwlist[i].fw, err);
>  
>  	return err;
>  }
> [...]
rest looks good. [Altough, I won't be able to test it in the forthcoming weeks]

Best Regards,
	Chr
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux