All of lore.kernel.org
 help / color / mirror / Atom feed
From: Florian Fainelli <florian@openwrt.org>
To: Michael Buesch <mb@bu3sch.de>
Cc: bcm43xx-dev@lists.berlios.de,
	"linux-wireless" <linux-wireless@vger.kernel.org>,
	Oncaphillis <oncaphillis@snafu.de>,
	Larry Finger <Larry.Finger@lwfinger.net>
Subject: Re: [PATCH RFC] ssb: Generic SPROM override for devices without SPROM
Date: Fri, 20 Nov 2009 12:38:07 +0100	[thread overview]
Message-ID: <200911201238.08005.florian@openwrt.org> (raw)
In-Reply-To: <200911201212.19588.mb@bu3sch.de>

Hi Michael,

On Friday 20 November 2009 12:12:19 Michael Buesch wrote:
> This patch adds a generic mechanism for overriding the SPROM mechanism
> on devices without SPROM hardware.
> 
> There currently is a major problem with this:
> It tries to deduce a MAC address from various hardware parameters. But
> currently it will result in the same MAC address for machines of the same
> type. Does somebody have an idea of some device-instance specific serial
> number or something similar that could be hashed into the MAC?

BCM63xx is one of the users of this mechanism and we have a pool of MAC 
addresses that we can use at a given Flash offset, for this we have a function 
which will read the MAC address pool and know how many MACs we are currently 
using.

The BCM63xx board code does the following :

- declare and initialize a "safe" sprom v2 structure (bcm63xx_sprom)
- call board_get_mac_address and store the mac in bcm63xx_sprom.il0mac
- memcpy il0mac to et0mac and et1mac in bcm63xx_sprom
- call ssb_arch_set_fallback_sprom(&bcm63xx_sprom) to register our "fake" 
sprom

I got some comments below:

> 
> 
> Index: wireless-testing/drivers/ssb/pci.c
> ===================================================================
> --- wireless-testing.orig/drivers/ssb/pci.c	2009-11-19 18:34:42.000000000
>  +0100 +++ wireless-testing/drivers/ssb/pci.c	2009-11-19 18:37:27.000000000
>  +0100 @@ -252,6 +252,9 @@ static int sprom_do_read(struct ssb_bus
>  {
>  	int i;
> 
> +	if (!bus->sprom_size)
> +		return -ENODEV;
> +
>  	for (i = 0; i < bus->sprom_size; i++)
>  		sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));
> 
> @@ -265,6 +268,9 @@ static int sprom_do_write(struct ssb_bus
>  	u32 spromctl;
>  	u16 size = bus->sprom_size;
> 
> +	if (!size)
> +		return -ENODEV;
> +
>  	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power!
>  Please stand by...\n"); err = pci_read_config_dword(pdev, SSB_SPROMCTL,
>  &spromctl);
>  	if (err)
> @@ -616,10 +622,17 @@ static int sprom_extract(struct ssb_bus
>  static int ssb_pci_sprom_get(struct ssb_bus *bus,
>  			     struct ssb_sprom *sprom)
>  {
> -	const struct ssb_sprom *fallback;
> -	int err = -ENOMEM;
> +	int err;
>  	u16 *buf;
> 
> +	bus->sprom_size = 0;
> +	err = ssb_find_sprom_override(bus, sprom);
> +	if (!err) {
> +		ssb_printk(KERN_INFO PFX "Overriding SPROM image\n");
> +		return 0;
> +	}
> +
> +	err = -ENOMEM;
>  	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
>  	if (!buf)
>  		goto out;
> @@ -637,22 +650,12 @@ static int ssb_pci_sprom_get(struct ssb_
>  		sprom_do_read(bus, buf);
>  		err = sprom_check_crc(buf, bus->sprom_size);
>  		if (err) {
> -			/* All CRC attempts failed.
> -			 * Maybe there is no SPROM on the device?
> -			 * If we have a fallback, use that. */
> -			fallback = ssb_get_fallback_sprom();
> -			if (fallback) {
> -				memcpy(sprom, fallback, sizeof(*sprom));
> -				err = 0;
> -				goto out_free;
> -			}
>  			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
>  				   " SPROM CRC (corrupt SPROM)\n");
>  		}
>  	}
>  	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
> 
> -out_free:
>  	kfree(buf);
>  out:
>  	return err;
> Index: wireless-testing/drivers/ssb/sprom.c
> ===================================================================
> --- wireless-testing.orig/drivers/ssb/sprom.c	2009-11-19 18:34:42.000000000
>  +0100 +++ wireless-testing/drivers/ssb/sprom.c	2009-11-19
>  18:37:27.000000000 +0100 @@ -13,8 +13,13 @@
> 
>  #include "ssb_private.h"
> 
> +#include <linux/list.h>
> +#include <linux/spinlock.h>
> 
> -static const struct ssb_sprom *fallback_sprom;
> +
> +/* List of registered SPROM overrides. */
> +static LIST_HEAD(override_list);
> +static DEFINE_SPINLOCK(override_list_lock);
> 
> 
>  static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
> @@ -135,35 +140,34 @@ out:
>  	return err ? err : count;
>  }
> 
> -/**
> - * ssb_arch_set_fallback_sprom - Set a fallback SPROM for use if no SPROM
>  is found. - *
> - * @sprom: The SPROM data structure to register.
> - *
> - * With this function the architecture implementation may register a
>  fallback - * SPROM data structure. The fallback is only used for PCI based
>  SSB devices, - * where no valid SPROM can be found in the shadow
>  registers.
> - *
> - * This function is useful for weird architectures that have a half-assed
>  SSB device - * hardwired to their PCI bus.
> - *
> - * Note that it does only work with PCI attached SSB devices. PCMCIA
>  devices currently - * don't use this fallback.
> - * Architectures must provide the SPROM for native SSB devices anyway,
> - * so the fallback also isn't used for native devices.
> - *
> - * This function is available for architecture code, only. So it is not
>  exported. - */
> -int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom)
> -{
> -	if (fallback_sprom)
> -		return -EEXIST;
> -	fallback_sprom = sprom;
> +void ssb_register_sprom_override(struct ssb_sprom_override *ovr)
> +{
> +	spin_lock(&override_list_lock);
> +	list_add_tail(&ovr->list, &override_list);
> +	spin_unlock(&override_list_lock);
> +}
> +EXPORT_SYMBOL(ssb_register_sprom_override);
> 
> -	return 0;
> +void ssb_unregister_sprom_override(struct ssb_sprom_override *ovr)
> +{
> +	spin_lock(&override_list_lock);
> +	list_del(&ovr->list);
> +	spin_unlock(&override_list_lock);
>  }
> +EXPORT_SYMBOL(ssb_unregister_sprom_override);
> 
> -const struct ssb_sprom *ssb_get_fallback_sprom(void)
> +int ssb_find_sprom_override(struct ssb_bus *bus, struct ssb_sprom *buf)
>  {
> -	return fallback_sprom;
> +	struct ssb_sprom_override *ovr;
> +	int err = -ENODEV;
> +
> +	spin_lock(&override_list_lock);
> +	list_for_each_entry(ovr, &override_list, list) {
> +		err = ovr->probe(bus, buf);
> +		if (!err)
> +			break;
> +	}
> +	spin_unlock(&override_list_lock);
> +
> +	return err;
>  }
> Index: wireless-testing/drivers/ssb/ssb_private.h
> ===================================================================
> --- wireless-testing.orig/drivers/ssb/ssb_private.h	2009-11-19
>  18:34:42.000000000 +0100 +++
>  wireless-testing/drivers/ssb/ssb_private.h	2009-11-19 18:37:27.000000000
>  +0100 @@ -171,7 +171,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_
>  			     const char *buf, size_t count,
>  			     int (*sprom_check_crc)(const u16 *sprom, size_t size),
>  			     int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom));
> -extern const struct ssb_sprom *ssb_get_fallback_sprom(void);
> +extern int ssb_find_sprom_override(struct ssb_bus *bus, struct ssb_sprom
>  *buf);
> 
> 
>  /* core.c */
> Index: wireless-testing/include/linux/ssb/ssb.h
> ===================================================================
> --- wireless-testing.orig/include/linux/ssb/ssb.h	2009-11-19
>  18:34:42.000000000 +0100 +++
>  wireless-testing/include/linux/ssb/ssb.h	2009-11-19 18:37:27.000000000
>  +0100 @@ -394,9 +394,20 @@ extern int ssb_bus_sdiobus_register(stru
> 
>  extern void ssb_bus_unregister(struct ssb_bus *bus);
> 
> -/* Set a fallback SPROM.
> - * See kdoc at the function definition for complete documentation. */
> -extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
> +/** struct ssb_sprom_override - SPROM override handler
> + * @probe: Callback function used to probe for a SPROM override.
> + *	Puts the override image into "buf" and returns 0.
> + *	If there's no need to override the image, nonzero is returned.
> + *	This callback runs in atomic context.
> + * @list: Used internally in ssb. Do not use in the device driver.
> + */
> +struct ssb_sprom_override {
> +	int (*probe)(struct ssb_bus *bus, struct ssb_sprom *buf);
> +	struct list_head list;
> +};
> +
> +extern void ssb_register_sprom_override(struct ssb_sprom_override *ovr);
> +extern void ssb_unregister_sprom_override(struct ssb_sprom_override *ovr);
> 
>  /* Suspend a SSB bus.
>   * Call this from the parent bus suspend routine. */
> Index: wireless-testing/drivers/ssb/b43_pci_bridge.c
> ===================================================================
> --- wireless-testing.orig/drivers/ssb/b43_pci_bridge.c	2009-11-19
>  18:34:42.000000000 +0100 +++
>  wireless-testing/drivers/ssb/b43_pci_bridge.c	2009-11-20
>  12:04:09.000000000 +0100 @@ -5,13 +5,15 @@
>   * because of its small size we include it in the SSB core
>   * instead of creating a standalone module.
>   *
> - * Copyright 2007  Michael Buesch <mb@bu3sch.de>
> + * Copyright 2007-2009  Michael Buesch <mb@bu3sch.de>
>   *
>   * Licensed under the GNU/GPL. See COPYING for details.
>   */
> 
>  #include <linux/pci.h>
>  #include <linux/ssb/ssb.h>
> +#include <linux/etherdevice.h>
> +#include <linux/jhash.h>
> 
>  #include "ssb_private.h"
> 
> @@ -36,6 +38,76 @@ static const struct pci_device_id b43_pc
>  };
>  MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl);
> 
> +
> +static void pcidev_deduce_mac_address(struct pci_dev *pdev,
> +				      struct ssb_sprom *sprom,
> +				      const char *oui)
> +{
> +	u32 hash = 0x63E72B6D;
> +
> +	hash = jhash(&pdev->device, sizeof(pdev->device), hash);
> +	hash = jhash(&pdev->subsystem_device, sizeof(pdev->subsystem_device),
>  hash); +	hash = jhash(&pdev->devfn, sizeof(pdev->devfn), hash);
> +	//TODO: Need machine specific seed
> +
> +	sprom->il0mac[3] = hash;
> +	sprom->il0mac[4] = hash >> 8;
> +	sprom->il0mac[5] = hash >> 16;
> +	memcpy(sprom->il0mac, oui, 3);
> +	memcpy(sprom->et0mac, sprom->il0mac, ETH_ALEN);
> +	memcpy(sprom->et1mac, sprom->il0mac, ETH_ALEN);
> +}

Why not call once random_ether_addr instead of using some kind of hash? Is it 
because you
want the same MAC from a reboot to another?


> +
> +#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice)		( \
> +	(pdev->vendor == PCI_VENDOR_ID_##_vendor) &&			\
> +	(pdev->device == _device) &&					\
> +	(pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) &&	\
> +	(pdev->subsystem_device == _subdevice)				)
> +
> +static int b43_sprom_override_probe(struct ssb_bus *bus,
> +				    struct ssb_sprom *sprom)
> +{
> +	struct pci_dev *pdev;
> +
> +	if (bus->bustype != SSB_BUSTYPE_PCI)
> +		return -ENODEV;
> +	pdev = bus->host_pci;
> +
> +	if (IS_PDEV(pdev, BROADCOM, 0x4315, FOXCONN, 0xE01B)) {
> +		static const struct ssb_sprom image = {
> +			.revision		= 0x02,
> +			.board_rev		= 0x17,
> +			.country_code		= 0x0,
> +			.ant_available_bg 	= 0x3,
> +			.pa0b0			= 0x15ae,
> +			.pa0b1			= 0xfa85,
> +			.pa0b2			= 0xfe8d,
> +			.pa1b0			= 0xffff,
> +			.pa1b1			= 0xffff,
> +			.pa1b2			= 0xffff,
> +			.gpio0			= 0xff,
> +			.gpio1			= 0xff,
> +			.gpio2			= 0xff,
> +			.gpio3			= 0xff,
> +			.maxpwr_bg		= 0x004c,
> +			.itssi_bg		= 0x00,
> +			.boardflags_lo		= 0x2848,
> +			.boardflags_hi		= 0x0000,
> +		};//FIXME This image is not the right one.

Ok, so for BCM63xx I would no longer have to declare my SPROM, fine.

> +
> +		memcpy(sprom, &image, sizeof(*sprom));
> +		pcidev_deduce_mac_address(pdev, sprom, "\x00\x15\x58");
> +
> +		return 0;
> +	}
> +
> +	return -ENODEV;

What about being able to override the MAC address directly in the case of 
BCM63XX for instance? If I copy the MAC address from board_get_mac_addres, 
pcidev_deduce_mac_address would overwrite it anyway right?

> +}
> +
> +static struct ssb_sprom_override b43_sprom_override = {
> +	.probe		= b43_sprom_override_probe,
> +};
> +
>  static struct pci_driver b43_pci_bridge_driver = {
>  	.name = "b43-pci-bridge",
>  	.id_table = b43_pci_bridge_tbl,
> @@ -44,10 +116,20 @@ static struct pci_driver b43_pci_bridge_
> 
>  int __init b43_pci_ssb_bridge_init(void)
>  {
> -	return ssb_pcihost_register(&b43_pci_bridge_driver);
> +	int err;
> +
> +	ssb_register_sprom_override(&b43_sprom_override);
> +	err = ssb_pcihost_register(&b43_pci_bridge_driver);
> +	if (err) {
> +		ssb_unregister_sprom_override(&b43_sprom_override);
> +		return err;
> +	}
> +
> +	return 0;
>  }
> 
>  void __exit b43_pci_ssb_bridge_exit(void)
>  {
>  	ssb_pcihost_unregister(&b43_pci_bridge_driver);
> +	ssb_unregister_sprom_override(&b43_sprom_override);
>  }
> 

-- 
--
WBR, Florian

  reply	other threads:[~2009-11-20 11:39 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-11-20 11:12 [PATCH RFC] ssb: Generic SPROM override for devices without SPROM Michael Buesch
2009-11-20 11:38 ` Florian Fainelli [this message]
2009-11-20 11:44   ` Michael Buesch
2009-11-20 14:05 ` Larry Finger
2009-11-20 14:11   ` Michael Buesch
2009-11-20 14:16     ` Florian Fainelli
2009-11-20 14:51     ` Larry Finger
2009-11-20 17:19     ` Larry Finger
2009-11-20 14:34   ` Stefan Lippers-Hollmann
2009-11-20 14:50   ` Ehud Gavron
2009-11-20 14:55     ` Larry Finger
2009-11-24  8:51 ` Oncaphillis
2009-11-24 10:52   ` Michael Buesch

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200911201238.08005.florian@openwrt.org \
    --to=florian@openwrt.org \
    --cc=Larry.Finger@lwfinger.net \
    --cc=bcm43xx-dev@lists.berlios.de \
    --cc=linux-wireless@vger.kernel.org \
    --cc=mb@bu3sch.de \
    --cc=oncaphillis@snafu.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.