From: Michael Buesch <mb@bu3sch.de>
To: bcm43xx-dev@lists.berlios.de
Cc: "linux-wireless" <linux-wireless@vger.kernel.org>,
Oncaphillis <oncaphillis@snafu.de>,
Larry Finger <Larry.Finger@lwfinger.net>
Subject: [PATCH RFC] ssb: Generic SPROM override for devices without SPROM
Date: Fri, 20 Nov 2009 12:12:19 +0100 [thread overview]
Message-ID: <200911201212.19588.mb@bu3sch.de> (raw)
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?
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);
+}
+
+#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.
+
+ memcpy(sprom, &image, sizeof(*sprom));
+ pcidev_deduce_mac_address(pdev, sprom, "\x00\x15\x58");
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+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);
}
--
Greetings, Michael.
next reply other threads:[~2009-11-20 11:16 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-11-20 11:12 Michael Buesch [this message]
2009-11-20 11:38 ` [PATCH RFC] ssb: Generic SPROM override for devices without SPROM Florian Fainelli
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=200911201212.19588.mb@bu3sch.de \
--to=mb@bu3sch.de \
--cc=Larry.Finger@lwfinger.net \
--cc=bcm43xx-dev@lists.berlios.de \
--cc=linux-wireless@vger.kernel.org \
--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.