lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1258746786.26965.83.camel@localhost.localdomain>
Date:	Fri, 20 Nov 2009 11:53:06 -0800
From:	Dan Williams <dcbw@...hat.com>
To:	Christian Lamparter <chunkeey@...glemail.com>
Cc:	linux-wireless@...r.kernel.org, netdev@...r.kernel.org
Subject: Re: [RFC] p54: software hot-swap eeprom image

On Fri, 2009-11-20 at 20:27 +0100, Christian Lamparter wrote:
> This (aggregated) patch adds a new sysfs file "eeprom_overwrite"
> which can be used to upload an customized eeprom image to the
> driver during normal operation.
> 
> The old wlanX device will quickly vanish. And if the new EEPROM
> image passes all sanity checks the device will reappear.
> 
> Hopefully, this proves to be an adequate solution for everyone
> with a bad/missing EEPROM data/chip.

Honestly I wonder if this would be better done with ethtool.  There's
already ethtool --eeprom-dump, would it be so hard to do an ethtool
--eeprom-load ?

I can see a number of devices doing this, so in my mind it makes sense
to have a generic facility for this sort of thing.  But then again it's
not so common an operation and its fraught with danger :)  Libertas
could potentially use this, but libertas has *two* EEPROM images that it
might need to update.  So if ethtool did grow --eeprom-load we may want
to account for more than one eeprom image.

What do people think?

Dan

> ---
> there are plenty of bugs left!
> 
> would be nice to hear from p54spi & p54pci folks!
> 
> Regards,
> 	Chr
> ---
> diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c
> index 8e3818f..e5c94d9 100644
> --- a/drivers/net/wireless/p54/eeprom.c
> +++ b/drivers/net/wireless/p54/eeprom.c
> @@ -121,25 +121,20 @@ static int p54_generate_band(struct ieee80211_hw *dev,
>  			     enum ieee80211_band band)
>  {
>  	struct p54_common *priv = dev->priv;
> -	struct ieee80211_supported_band *tmp, *old;
> +	struct ieee80211_supported_band *old_band, *new_band;
> +	struct ieee80211_channel *tmp, *old_chan, *new_chan;
>  	unsigned int i, j;
> -	int ret = -ENOMEM;
> +	int old_chan_num, ret = 0;
>  
>  	if ((!list->entries) || (!list->band_channel_num[band]))
>  		return -EINVAL;
>  
> -	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
> -	if (!tmp)
> -		goto err_out;
> -
> -	tmp->channels = kzalloc(sizeof(struct ieee80211_channel) *
> -				list->band_channel_num[band], GFP_KERNEL);
> -	if (!tmp->channels)
> -		goto err_out;
> -
> -	ret = p54_fill_band_bitrates(dev, tmp, band);
> -	if (ret)
> -		goto err_out;
> +	tmp = kzalloc(sizeof(struct ieee80211_channel) *
> +		list->band_channel_num[band], GFP_KERNEL);
> +	if (!tmp) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
>  
>  	for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
>  			   (i < list->entries); i++) {
> @@ -161,8 +156,8 @@ static int p54_generate_band(struct ieee80211_hw *dev,
>  			continue;
>  		}
>  
> -		tmp->channels[j].band = list->channels[i].band;
> -		tmp->channels[j].center_freq = list->channels[i].freq;
> +		tmp[j].band = list->channels[i].band;
> +		tmp[j].center_freq = list->channels[i].freq;
>  		j++;
>  	}
>  
> @@ -172,25 +167,60 @@ static int p54_generate_band(struct ieee80211_hw *dev,
>  		       "2 GHz" : "5 GHz");
>  
>  		ret = -ENODATA;
> -		goto err_out;
> +		goto out;
>  	}
>  
> -	tmp->n_channels = j;
> -	old = priv->band_table[band];
> -	priv->band_table[band] = tmp;
> -	if (old) {
> -		kfree(old->channels);
> -		kfree(old);
> +	old_band = priv->band_table[band];
> +	if (old_band) {
> +		old_chan_num = old_band->n_channels;
> +		old_chan = old_band->channels;
> +		old_band->n_channels = 0;
> +		old_band->n_bitrates = 0;
> +	} else {
> +		old_chan_num = 0;
> +		old_chan = NULL;
>  	}
>  
> -	return 0;
> +	if (j < old_chan_num) {
> +		printk(KERN_ERR "%s: Channel list can only be extended.\n",
> +		       wiphy_name(dev->wiphy));
> +		goto out;
> +	}
> +
> +	new_band = krealloc(old_band, sizeof(*new_band), GFP_KERNEL);
> +	if (!new_band) {
> +		printk(KERN_ERR "%s: Unable to modify band table.\n",
> +		       wiphy_name(dev->wiphy));
> +
> +		goto out;
> +	}
> +
> +	new_band->band = band;
> +	new_band->ht_cap.ht_supported = false;
>  
> -err_out:
> -	if (tmp) {
> -		kfree(tmp->channels);
> -		kfree(tmp);
> +	new_chan = krealloc(old_chan, j * sizeof(struct ieee80211_channel),
> +			    GFP_KERNEL);
> +
> +	if (!new_chan) {
> +		printk(KERN_ERR "%s: Unable to modify band channel table.\n",
> +		       wiphy_name(dev->wiphy));
> +
> +		goto out;
>  	}
>  
> +	memcpy(new_chan, tmp, j * sizeof(struct ieee80211_channel));
> +
> +	new_band->channels = new_chan;
> +	new_band->n_channels = j;
> +
> +	ret = p54_fill_band_bitrates(dev, new_band, band);
> +	if (ret)
> +		goto out;
> +
> +	priv->band_table[band] = new_band;
> +
> +out:
> +	kfree(tmp);
>  	return ret;
>  }
>  
> @@ -533,7 +563,19 @@ static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src,
>  	return dst;
>  }
>  
> -int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
> +static void p54_reset_phydata(struct p54_common *priv)
> +{
> +	memset(&priv->rssical_db, 0, sizeof(priv->rssical_db));
> +	priv->iq_autocal_len = 0;
> +	kfree(priv->output_limit);
> +	kfree(priv->curve_data);
> +	kfree(priv->iq_autocal);
> +	priv->output_limit = NULL;
> +	priv->curve_data = NULL;
> +	priv->iq_autocal = NULL;
> +}
> +
> +int p54_parse_eeprom(struct ieee80211_hw *dev, const void *eeprom, int len)
>  {
>  	struct p54_common *priv = dev->priv;
>  	struct eeprom_pda_wrap *wrap;
> @@ -543,10 +585,14 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  	int err;
>  	u8 *end = (u8 *)eeprom + len;
>  	u16 synth = 0;
> +	bool has_rssical = false, has_mac = false, has_country = false;
>  
>  	wrap = (struct eeprom_pda_wrap *) eeprom;
>  	entry = (void *)wrap->data + le16_to_cpu(wrap->len);
>  
> +	mutex_lock(&priv->conf_mutex);
> +	p54_reset_phydata(priv);
> +
>  	/* verify that at least the entry length/code fits */
>  	while ((u8 *)entry <= end - sizeof(*entry)) {
>  		entry_len = le16_to_cpu(entry->len);
> @@ -558,21 +604,34 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  
>  		switch (le16_to_cpu(entry->code)) {
>  		case PDR_MAC_ADDRESS:
> +			if (has_mac)
> +				break;
> +
> +			has_mac = true;
> +
>  			if (data_len != ETH_ALEN)
>  				break;
> +
>  			SET_IEEE80211_PERM_ADDR(dev, entry->data);
>  			break;
> +
>  		case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS:
>  			if (priv->output_limit)
>  				break;
> +
>  			err = p54_convert_output_limits(dev, entry->data,
>  							data_len);
>  			if (err)
>  				goto err;
>  			break;
> +
>  		case PDR_PRISM_PA_CAL_CURVE_DATA: {
>  			struct pda_pa_curve_data *curve_data =
>  				(struct pda_pa_curve_data *)entry->data;
> +
> +			if (priv->curve_data)
> +				break;
> +
>  			if (data_len < sizeof(*curve_data)) {
>  				err = -EINVAL;
>  				goto err;
> @@ -597,7 +656,11 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  				goto err;
>  			}
>  			break;
> +
>  		case PDR_PRISM_ZIF_TX_IQ_CALIBRATION:
> +			if (priv->iq_autocal)
> +				break;
> +
>  			priv->iq_autocal = kmalloc(data_len, GFP_KERNEL);
>  			if (!priv->iq_autocal) {
>  				err = -ENOMEM;
> @@ -607,11 +670,22 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  			memcpy(priv->iq_autocal, entry->data, data_len);
>  			priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry);
>  			break;
> +
>  		case PDR_DEFAULT_COUNTRY:
> +			if (has_country)
> +				break;
> +
> +			has_country = true;
> +
>  			p54_parse_default_country(dev, entry->data, data_len);
>  			break;
> +
>  		case PDR_INTERFACE_LIST:
>  			tmp = entry->data;
> +
> +			if (synth)
> +				break;
> +
>  			while ((u8 *)tmp < entry->data + data_len) {
>  				struct exp_if *exp_if = tmp;
>  				if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000))
> @@ -619,22 +693,38 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  				tmp += sizeof(*exp_if);
>  			}
>  			break;
> +
>  		case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
> +			if (priv->version)
> +				break;
> +
>  			if (data_len < 2)
>  				break;
>  			priv->version = *(u8 *)(entry->data + 1);
>  			break;
> +
>  		case PDR_RSSI_LINEAR_APPROXIMATION:
>  		case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
>  		case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
> +			if (has_rssical)
> +				break;
> +
> +			has_rssical = true;
> +
>  			p54_parse_rssical(dev, entry->data, data_len,
>  					  le16_to_cpu(entry->code));
>  			break;
> +
>  		case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: {
>  			__le16 *src = (void *) entry->data;
>  			s16 *dst = (void *) &priv->rssical_db;
>  			int i;
>  
> +			if (has_rssical)
> +				break;
> +
> +			has_rssical = true;
> +
>  			if (data_len != sizeof(priv->rssical_db)) {
>  				err = -EINVAL;
>  				goto err;
> @@ -644,24 +734,30 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  				*(dst++) = (s16) le16_to_cpu(*(src++));
>  			}
>  			break;
> +
>  		case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: {
>  			struct pda_custom_wrapper *pda = (void *) entry->data;
>  			if (priv->output_limit || data_len < sizeof(*pda))
>  				break;
> +
>  			priv->output_limit = p54_convert_db(pda, data_len);
>  			}
>  			break;
> +
>  		case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: {
>  			struct pda_custom_wrapper *pda = (void *) entry->data;
>  			if (priv->curve_data || data_len < sizeof(*pda))
>  				break;
> +
>  			priv->curve_data = p54_convert_db(pda, data_len);
>  			}
>  			break;
> +
>  		case PDR_END:
>  			/* make it overrun */
>  			entry_len = len;
>  			break;
> +
>  		default:
>  			break;
>  		}
> @@ -670,7 +766,11 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  	}
>  
>  	if (!synth || !priv->iq_autocal || !priv->output_limit ||
> -	    !priv->curve_data) {
> +	    !priv->curve_data || !has_rssical) {
> +		printk(KERN_INFO "%d %p %p %p %d\n",
> +			synth, priv->iq_autocal, priv->output_limit,
> +			priv->curve_data, has_rssical);
> +
>  		printk(KERN_ERR "%s: not all required entries found in eeprom!\n",
>  			wiphy_name(dev->wiphy));
>  		err = -EINVAL;
> @@ -708,18 +808,18 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
>  		wiphy_name(dev->wiphy),	dev->wiphy->perm_addr, priv->version,
>  		p54_rf_chips[priv->rxhw]);
>  
> +	mutex_unlock(&priv->conf_mutex);
> +
>  	return 0;
>  
>  err:
> -	kfree(priv->iq_autocal);
> -	kfree(priv->output_limit);
> -	kfree(priv->curve_data);
> -	priv->iq_autocal = NULL;
> -	priv->output_limit = NULL;
> -	priv->curve_data = NULL;
> +	p54_reset_phydata(priv);
>  
>  	printk(KERN_ERR "%s: eeprom parse failed!\n",
>  		wiphy_name(dev->wiphy));
> +
> +	mutex_unlock(&priv->conf_mutex);
> +
>  	return err;
>  }
>  EXPORT_SYMBOL_GPL(p54_parse_eeprom);
> @@ -753,8 +853,39 @@ int p54_read_eeprom(struct ieee80211_hw *dev)
>  	}
>  
>  	ret = p54_parse_eeprom(dev, eeprom, offset);
> +
>  free:
>  	kfree(eeprom);
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(p54_read_eeprom);
> +
> +ssize_t p54_overwrite_eeprom(struct ieee80211_hw *dev,
> +			     const char *buf, size_t len)
> +{
> +	struct p54_common *priv = dev->priv;
> +	int err;
> +
> +	if (len < 64 || len > 0x2020)
> +		return -EINVAL;
> +
> +	mutex_lock(&priv->sysfs_mutex);
> +	p54_unregister_common(dev);
> +
> +	err = p54_parse_eeprom(dev, buf, len);
> +	if (err)
> +		goto out;
> +
> +	err = p54_register_common(dev, priv->pdev);
> +	if (err)
> +		goto out;
> +
> +out:
> +
> +	if (err)
> +		dev_err(priv->pdev, "failed to (re)-register device.\n");
> +
> +	mutex_unlock(&priv->sysfs_mutex);
> +	return (err) ? err : len;
> +}
> +EXPORT_SYMBOL_GPL(p54_overwrite_eeprom);
> diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
> index 18012db..e1395d4 100644
> --- a/drivers/net/wireless/p54/main.c
> +++ b/drivers/net/wireless/p54/main.c
> @@ -582,6 +582,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
>  	dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
>  
>  	mutex_init(&priv->conf_mutex);
> +	mutex_init(&priv->sysfs_mutex);
>  	mutex_init(&priv->eeprom_mutex);
>  	init_completion(&priv->eeprom_comp);
>  	init_completion(&priv->beacon_comp);
> @@ -596,6 +597,9 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
>  	struct p54_common *priv = dev->priv;
>  	int err;
>  
> +	if (priv->registered)
> +		return -EBUSY;
> +
>  	err = ieee80211_register_hw(dev);
>  	if (err) {
>  		dev_err(pdev, "Cannot register device (%d).\n", err);
> @@ -607,8 +611,9 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
>  	if (err)
>  		return err;
>  #endif /* CONFIG_P54_LEDS */
> -
> +	priv->pdev = pdev;
>  	dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
> +	priv->registered = true;
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(p54_register_common);
> @@ -618,6 +623,11 @@ void p54_free_common(struct ieee80211_hw *dev)
>  	struct p54_common *priv = dev->priv;
>  	unsigned int i;
>  
> +	BUG_ON(priv->registered);
> +
> +	mutex_destroy(&priv->conf_mutex);
> +	mutex_destroy(&priv->eeprom_mutex);
> +
>  	for (i = 0; i < IEEE80211_NUM_BANDS; i++)
>  		kfree(priv->band_table[i]);
>  
> @@ -637,12 +647,14 @@ void p54_unregister_common(struct ieee80211_hw *dev)
>  {
>  	struct p54_common *priv = dev->priv;
>  
> +	if (!priv->registered)
> +		return ;
> +
>  #ifdef CONFIG_P54_LEDS
>  	p54_unregister_leds(priv);
>  #endif /* CONFIG_P54_LEDS */
>  
> -	ieee80211_unregister_hw(dev);
> -	mutex_destroy(&priv->conf_mutex);
> -	mutex_destroy(&priv->eeprom_mutex);
> +	ieee80211_unregister_hw(priv->hw);
> +	priv->registered = false;
>  }
>  EXPORT_SYMBOL_GPL(p54_unregister_common);
> diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
> index 1afc394..3e097e0 100644
> --- a/drivers/net/wireless/p54/p54.h
> +++ b/drivers/net/wireless/p54/p54.h
> @@ -159,6 +159,7 @@ struct p54_led_dev {
>  
>  struct p54_common {
>  	struct ieee80211_hw *hw;
> +	struct device *pdev;
>  	struct ieee80211_vif *vif;
>  	void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
>  	int (*open)(struct ieee80211_hw *dev);
> @@ -166,6 +167,7 @@ struct p54_common {
>  	struct sk_buff_head tx_pending;
>  	struct sk_buff_head tx_queue;
>  	struct mutex conf_mutex;
> +	bool registered;
>  
>  	/* memory management (as seen by the firmware) */
>  	u32 rx_start;
> @@ -233,14 +235,17 @@ struct p54_common {
>  	void *eeprom;
>  	struct completion eeprom_comp;
>  	struct mutex eeprom_mutex;
> +	struct mutex sysfs_mutex;
>  };
>  
>  /* interfaces for the drivers */
>  int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);
>  void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb);
>  int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw);
> -int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len);
> +int p54_parse_eeprom(struct ieee80211_hw *dev, const void *eeprom, int len);
>  int p54_read_eeprom(struct ieee80211_hw *dev);
> +ssize_t p54_overwrite_eeprom(struct ieee80211_hw *dev,
> +			    const char *eeprom, size_t len);
>  
>  struct ieee80211_hw *p54_init_common(size_t priv_data_len);
>  int p54_register_common(struct ieee80211_hw *dev, struct device *pdev);
> diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
> index e3a4c90..b8040e4 100644
> --- a/drivers/net/wireless/p54/p54pci.c
> +++ b/drivers/net/wireless/p54/p54pci.c
> @@ -466,6 +466,18 @@ static int p54p_open(struct ieee80211_hw *dev)
>  	return 0;
>  }
>  
> +static ssize_t p54p_sysfs_overwrite_eeprom(struct device *dev,
> +					   struct device_attribute *attr,
> +					   const char *buf, size_t count)
> +{
> +	struct p54p_priv *priv = dev_get_drvdata(dev);
> +
> +	return p54_overwrite_eeprom(priv->common.hw, buf, count);
> +}
> +
> +static DEVICE_ATTR(eeprom_overwrite, S_IWUSR,
> +		   NULL, p54p_sysfs_overwrite_eeprom);
> +
>  static int __devinit p54p_probe(struct pci_dev *pdev,
>  				const struct pci_device_id *id)
>  {
> @@ -561,6 +573,13 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
>  	if (err)
>  		goto err_free_common;
>  
> +	err = device_create_file(&pdev->dev, &dev_attr_eeprom_overwrite);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to create sysfs file "
> +				    "eeprom_overwrite.\n");
> +		goto err_free_common;
> +	}
> +
>  	return 0;
>  
>   err_free_common:
> @@ -591,6 +610,8 @@ static void __devexit p54p_remove(struct pci_dev *pdev)
>  		return;
>  
>  	p54_unregister_common(dev);
> +	device_remove_file(&pdev->dev, &dev_attr_eeprom_overwrite);
> +
>  	priv = dev->priv;
>  	release_firmware(priv->firmware);
>  	pci_free_consistent(pdev, sizeof(*priv->ring_control),
> diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
> index afd26bf..6fb0863 100644
> --- a/drivers/net/wireless/p54/p54spi.c
> +++ b/drivers/net/wireless/p54/p54spi.c
> @@ -592,6 +592,18 @@ static void p54spi_op_stop(struct ieee80211_hw *dev)
>  	mutex_unlock(&priv->mutex);
>  }
>  
> +static ssize_t p54s_sysfs_overwrite_eeprom(struct device *dev,
> +					   struct device_attribute *attr,
> +					   const char *buf, size_t count)
> +{
> +	struct p54s_priv *priv = dev_get_drvdata(dev);
> +
> +	return p54_overwrite_eeprom(priv->common.hw, buf, count);
> +}
> +
> +static DEVICE_ATTR(eeprom_overwrite, S_IWUSR,
> +		   NULL, p54s_sysfs_overwrite_eeprom);
> +
>  static int __devinit p54spi_probe(struct spi_device *spi)
>  {
>  	struct p54s_priv *priv = NULL;
> @@ -667,7 +679,9 @@ static int __devinit p54spi_probe(struct spi_device *spi)
>  	if (ret)
>  		goto err_free_common;
>  
> -	return 0;
> +        ret = device_create_file(&spi->dev, &dev_attr_eeprom_overwrite);
> +
> +	return ret;
>  
>  err_free_common:
>  	p54_free_common(priv->hw);
> @@ -680,6 +694,9 @@ static int __devexit p54spi_remove(struct spi_device *spi)
>  
>  	p54_unregister_common(priv->hw);
>  
> +	device_remove_file(&spi->dev, &dev_attr_eeprom_overwrite);
> +	dev_set_drvdata(&spi->dev, NULL);
> +
>  	free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
>  
>  	gpio_free(p54spi_gpio_power);
> diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
> index 92af9b9..be699d6 100644
> --- a/drivers/net/wireless/p54/p54usb.c
> +++ b/drivers/net/wireless/p54/p54usb.c
> @@ -874,6 +874,19 @@ static void p54u_stop(struct ieee80211_hw *dev)
>  	return;
>  }
>  
> +static ssize_t p54u_sysfs_overwrite_eeprom(struct device *dev,
> +					   struct device_attribute *attr,
> +					   const char *buf, size_t count)
> +{
> +        struct ieee80211_hw *hw = (struct ieee80211_hw *)
> +                usb_get_intfdata(to_usb_interface(dev));
> +
> +	return p54_overwrite_eeprom(hw, buf, count);
> +}
> +
> +static DEVICE_ATTR(eeprom_overwrite, S_IWUSR,
> +                   NULL, p54u_sysfs_overwrite_eeprom);
> +
>  static int __devinit p54u_probe(struct usb_interface *intf,
>  				const struct usb_device_id *id)
>  {
> @@ -959,7 +972,9 @@ static int __devinit p54u_probe(struct usb_interface *intf,
>  	if (err)
>  		goto err_free_fw;
>  
> -	return 0;
> +	err = device_create_file(&intf->dev, &dev_attr_eeprom_overwrite);
> +
> +	return err;
>  
>  err_free_fw:
>  	release_firmware(priv->fw);
> @@ -982,6 +997,9 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
>  	p54_unregister_common(dev);
>  
>  	priv = dev->priv;
> +	device_remove_file(&intf->dev, &dev_attr_eeprom_overwrite); 
> +
> +	usb_set_intfdata(intf, NULL);
>  	usb_put_dev(interface_to_usbdev(intf));
>  	release_firmware(priv->fw);
>  	p54_free_common(dev);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists