Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [RFC PATCH] b43: Implement LP-PHY baseband table initialization
From: Michael Buesch @ 2009-08-10 17:19 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Larry Finger, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <200908101452.50072.mb@bu3sch.de>

On Monday 10 August 2009 14:52:49 Michael Buesch wrote:
> On Monday 10 August 2009 14:49:31 Gábor Stefanik wrote:
> > 2009/8/10 Michael Buesch <mb@bu3sch.de>:
> > > On Monday 10 August 2009 03:00:46 Gábor Stefanik wrote:
> > >> +static const u16 lpphy_sw_control_table[] = {
> > >> +     0x0128,
> > >> +     0x0128,
> > >> +     0x0009,
> > >> +     0x0009,
> > >> +     0x0028,
> > >> +     0x0028,
> > >
> > > Is it possible to use more than one value per line for all these tables?
> > > Make sure to make best use of the 80 columns limit.
> > > This would significantly decrease the patch/file length. (linewise)
> > >
> > > --
> > > Greetings, Michael.
> > >
> > 
> > Well, I converted the tables directly from the Wikitext on
> > bcm-v4.sipsolutions.net using regex search&replace - if you know a
> > good regex to convert the tables to use multiple values on each line,
> > please post it.
> > 
> 
> I have some hacky python scripts to parse and reformat these tables.
> I will post them in a few hours. I'll have to leave now. brb.
> 

Hm, I don't seem to have the scripts anymore. But python re is fairly simple.
Here's a hacky script to parse a table, perform some transformations on it and
print out the C defines:

#!/usr/bin/env python

import re
import sys

d = file(sys.argv[1]).readlines()


for line in d:
	r = re.compile(r"\|\| `0x([0-9A-Fa-f]+)` \|\| ([\w\s\(\)/]+) \|\|")
	m = r.match(line)
	if m:
		offset = int(m.group(1), 16)
		name = m.group(2)
		name = name.replace("workAround", "workaround")

		origname = name

		name = name.replace("(", " ")
		name = name.replace(")", " ")

		name = name.upper()
		name = name.strip()

		name = name.replace(" ", "_")
		name = name.replace("/", "_")
		name = name.replace("CONTROL", "CTL")
		name = name.replace("COMMON", "COMM")
		name = name.replace("CALIBRATION", "CALIB")
		name = name.replace("DEBUG", "DBG")
		name = name.replace("COUNTER", "CNT")
		name = name.replace("POWER", "PWR")
		name = name.replace("B_PHY", "B")
		name = name.replace("ADDRESS", "ADDR")
		name = name.replace("OUT_ENABLE", "OUTEN")
		name = name.replace("THRESHOLD", "THRES")
		name = name.replace("STATUS", "STAT")
		name = name.replace("COEFFICIENT", "COEFF")
		name = name.replace("INTERVAL", "INT")
		name = name.replace("TIMEOUT", "TO")
		name = name.replace("VALUE", "VAL")
		name = name.replace("SAMPLE", "SMPL")

		name = "B43_LPPHY_" + name

		nr_tabs = 5 - (len(name) / 8)
		tabs = "\t" * nr_tabs

		comment = origname

		if (offset & 0x400):
			accessor = "B43_PHY_OFDM"
		else:
			accessor = "B43_PHY_CCK"
		offset &= ~0x400;
		if (offset & ~0xFF):
			print "offset ERROR %X" % offset
			sys.exit(1)
		sys.stdout.write("#define %s%s%s(0x%02X)" % (name, tabs, accessor, offset))
		if comment:
			sys.stdout.write(" /* %s */" % comment)
		sys.stdout.write("\n")
	else:
		pass
		print "NO match " + line

-- 
Greetings, Michael.

^ permalink raw reply

* Re: [PATCH 1/2] mac802154: add a software MAC 802.15.4 implementation
From: Paul E. McKenney @ 2009-08-10 17:28 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov
  Cc: netdev, linux-zigbee-devel, linux-wireless, Sergey Lapin
In-Reply-To: <1249913800-10176-2-git-send-email-dbaryshkov@gmail.com>

On Mon, Aug 10, 2009 at 06:16:39PM +0400, Dmitry Eremin-Solenikov wrote:
> Some of available devices are just dump radios implementing IEEE 802.15.4
> PHY layer. This commit adds a common library that acts like an intermediate
> layer between our socket family and drivers for those dumb devices.
> 
> Currently this is data-only part (no commands, no beacons). Control
> interfaces will follow up shortly.
> 
> Note this implementaion is neither certified, nor feature complete!

One question below, otherwise looks plausible.  (I am not entirely sure
which lists are which...)

							Thanx, Paul

> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> Signed-off-by: Sergey Lapin <slapin@ossfans.org>
> ---
>  MAINTAINERS               |    1 +
>  include/linux/mac802154.h |   35 ++
>  include/net/mac802154.h   |  106 ++++++
>  net/Kconfig               |    1 +
>  net/Makefile              |    1 +
>  net/mac802154/Kconfig     |   17 +
>  net/mac802154/Makefile    |    4 +
>  net/mac802154/dev.c       |  924 +++++++++++++++++++++++++++++++++++++++++++++
>  net/mac802154/mac802154.h |   85 +++++
>  net/mac802154/mac_cmd.c   |   99 +++++
>  net/mac802154/mdev.c      |  295 +++++++++++++++
>  net/mac802154/mib.h       |   32 ++
>  net/mac802154/rx.c        |   98 +++++
>  13 files changed, 1698 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/mac802154.h
>  create mode 100644 include/net/mac802154.h
>  create mode 100644 net/mac802154/Kconfig
>  create mode 100644 net/mac802154/Makefile
>  create mode 100644 net/mac802154/dev.c
>  create mode 100644 net/mac802154/mac802154.h
>  create mode 100644 net/mac802154/mac_cmd.c
>  create mode 100644 net/mac802154/mdev.c
>  create mode 100644 net/mac802154/mib.h
>  create mode 100644 net/mac802154/rx.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d6befb2..5bdb64e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2529,6 +2529,7 @@ W:	http://apps.sourceforge.net/trac/linux-zigbee
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git
>  S:	Maintained
>  F:	net/ieee802154/
> +F:	net/mac802154/
>  F:	drivers/ieee802154/
> 
>  INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
> diff --git a/include/linux/mac802154.h b/include/linux/mac802154.h
> new file mode 100644
> index 0000000..e95e38d
> --- /dev/null
> +++ b/include/linux/mac802154.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (C) 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#ifndef LINUX_MAC802154_H
> +#define LINUX_MAC802154_H
> +
> +enum {
> +	IFLA_WPAN_UNSPEC,
> +	IFLA_WPAN_CHANNEL,
> +	IFLA_WPAN_PAN_ID,
> +	IFLA_WPAN_SHORT_ADDR,
> +	IFLA_WPAN_COORD_SHORT_ADDR,
> +	IFLA_WPAN_COORD_EXT_ADDR,
> +	__IFLA_WPAN_MAX,
> +};
> +
> +#define IFLA_WPAN_MAX	(__IFLA_WPAN_MAX - 1)
> +
> +#endif
> +
> diff --git a/include/net/mac802154.h b/include/net/mac802154.h
> new file mode 100644
> index 0000000..db76799
> --- /dev/null
> +++ b/include/net/mac802154.h
> @@ -0,0 +1,106 @@
> +/*
> + * IEEE802.15.4-2003 specification
> + *
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + */
> +#ifndef NET_MAC802154_H
> +#define NET_MAC802154_H
> +
> +struct ieee802154_dev {
> +	int	extra_tx_headroom; /* headroom to reserve for tx skb */
> +	void	*priv;		/* driver-specific data */
> +	u32	channel_mask;
> +	u8	current_channel;
> +	u32	flags; /* Flags for device to set */
> +	struct device *parent;
> +};
> +
> +/* Checksum is in hardware and is omitted from packet */
> +/**
> + * enum ieee802154_hw_flags - hardware flags
> + *
> + * These flags are used to indicate hardware capabilities to
> + * the stack. Generally, flags here should have their meaning
> + * done in a way that the simplest hardware doesn't need setting
> + * any particular flags. There are some exceptions to this rule,
> + * however, so you are advised to review these flags carefully.
> + *
> + * @IEEE802154_HW_OMIT_CKSUM:
> + *	Indicates that receiver omits FCS and transmitter will add
> + *	FCS on it's own.
> + *
> + * @IEEE802154_HW_AACK:
> + * 	Indicates that receiver will autorespond with ACK frames.
> + */
> +enum ieee802154_hw_flags {
> +	IEEE802154_HW_OMIT_CKSUM			= 1 << 0,
> +	IEEE802154_HW_AACK				= 1 << 1,
> +};
> +
> +struct sk_buff;
> +
> +/**
> + * struct ieee802154_ops - callbacks from mac802154 to the driver
> + *
> + * This structure contains various callbacks that the driver may
> + * handle or, in some cases, must handle, for example to transmit
> + * a frame.
> + *
> + * @start: Handler that 802.15.4 module calls for device initialisation.
> + * 	This function is called before the first interface is attached.
> + *
> + * @stop: Handler that 802.15.4 module calls for device cleanup
> + * 	This function is called after the last interface is removed.
> + *
> + * @tx: Handler that 802.15.4 module calls for each transmitted frame.
> + *      skb cntains the buffer starting from the IEEE 802.15.4 header.
> + *      The low-level driver should send the frame based on available
> + *      configuration.
> + *      This function should return zero or negative errno.
> + *
> + * @ed: Handler that 802.15.4 module calls for Energy Detection.
> + *      This function should place the value for detected energy
> + *      (usually device-dependant) in the level pointer and return
> + *      either zero or negative errno.
> + *
> + * @set_channel: Set radio for listening on specific channel.
> + *      Set the device for listening on specified channel.
> + *      Returns either zero, or negative errno.
> + */
> +struct ieee802154_ops {
> +	struct module	*owner;
> +	int		(*start)(struct ieee802154_dev *dev);
> +	void		(*stop)(struct ieee802154_dev *dev);
> +	int		(*xmit)(struct ieee802154_dev *dev,
> +						struct sk_buff *skb);
> +	int		(*ed)(struct ieee802154_dev *dev, u8 *level);
> +	int		(*set_channel)(struct ieee802154_dev *dev,
> +						int channel);
> +};
> +
> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> +						struct ieee802154_ops *ops);
> +int ieee802154_register_device(struct ieee802154_dev *dev);
> +void ieee802154_unregister_device(struct ieee802154_dev *dev);
> +void ieee802154_free_device(struct ieee802154_dev *dev);
> +
> +void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi);
> +void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb,
> +		u8 lqi);
> +#endif
> +
> diff --git a/net/Kconfig b/net/Kconfig
> index 7051b97..b42d325 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -180,6 +180,7 @@ source "net/econet/Kconfig"
>  source "net/wanrouter/Kconfig"
>  source "net/phonet/Kconfig"
>  source "net/ieee802154/Kconfig"
> +source "net/mac802154/Kconfig"
>  source "net/sched/Kconfig"
>  source "net/dcb/Kconfig"
> 
> diff --git a/net/Makefile b/net/Makefile
> index ba324ae..81115f6 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -61,6 +61,7 @@ ifneq ($(CONFIG_DCB),)
>  obj-y				+= dcb/
>  endif
>  obj-y				+= ieee802154/
> +obj-y				+= mac802154/
> 
>  ifeq ($(CONFIG_NET),y)
>  obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
> diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
> new file mode 100644
> index 0000000..2dd7f99
> --- /dev/null
> +++ b/net/mac802154/Kconfig
> @@ -0,0 +1,17 @@
> +config MAC802154
> +	tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
> +	depends on IEEE802154 && EXPERIMENTAL
> +	select CRC_CCITT
> +	---help---
> +	  This option enables the hardware independent IEEE 802.15.4
> +	  networking stack for SoftMAC devices (the ones implementing
> +	  only PHY level of IEEE 802.15.4 standard).
> +
> +	  Note: this implementation is neither certified, nor feature
> +	  complete! We do not garantee that it is compatible w/ other
> +	  implementations, etc.
> +
> +	  If you plan to use HardMAC IEEE 802.15.4 devices, you can
> +          say N here. Alternatievly you can say M to compile it as
> +	  module.
> +
> diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
> new file mode 100644
> index 0000000..07b7821
> --- /dev/null
> +++ b/net/mac802154/Makefile
> @@ -0,0 +1,4 @@
> +obj-$(CONFIG_MAC802154) +=	mac802154.o
> +mac802154-objs		:= rx.o mdev.o dev.o mac_cmd.o
> +
> +EXTRA_CFLAGS += -Wall -DDEBUG
> diff --git a/net/mac802154/dev.c b/net/mac802154/dev.c
> new file mode 100644
> index 0000000..b40f6fd
> --- /dev/null
> +++ b/net/mac802154/dev.c
> @@ -0,0 +1,924 @@
> +/*
> + * Copyright 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Sergey Lapin <slapin@ossfans.org>
> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
> + */
> +
> +#include <linux/net.h>
> +#include <linux/capability.h>
> +#include <linux/module.h>
> +#include <linux/if_arp.h>
> +#include <linux/rculist.h>
> +#include <linux/random.h>
> +#include <linux/crc-ccitt.h>
> +#include <linux/mac802154.h>
> +#include <net/rtnetlink.h>
> +
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +#include <net/ieee802154_netdev.h>
> +#include <net/ieee802154.h>
> +
> +#include "mac802154.h"
> +#include "mib.h"
> +
> +static int ieee802154_net_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv;
> +	priv = netdev_priv(dev);
> +
> +	if (!(priv->hw->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
> +		u16 crc = crc_ccitt(0, skb->data, skb->len);
> +		u8 *data = skb_put(skb, 2);
> +		data[0] = crc & 0xff;
> +		data[1] = crc >> 8;
> +	}
> +
> +	read_lock(&priv->mib_lock);
> +	phy_cb(skb)->chan = priv->chan;
> +	read_unlock(&priv->mib_lock);
> +
> +	skb->iif = dev->ifindex;
> +	skb->dev = priv->hw->netdev;
> +	dev->stats.tx_packets++;
> +	dev->stats.tx_bytes += skb->len;
> +
> +	dev->trans_start = jiffies;
> +	dev_queue_xmit(skb);
> +
> +	return 0;
> +}
> +
> +static int ieee802154_slave_open(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	int res = 0;
> +
> +	if (priv->hw->open_count++ == 0) {
> +		res = dev_open(priv->hw->netdev);
> +		WARN_ON(res);
> +		if (res)
> +			goto err;
> +	}
> +
> +	netif_start_queue(dev);
> +	return 0;
> +err:
> +	priv->hw->open_count--;
> +
> +	return res;
> +}
> +
> +static int ieee802154_slave_close(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	netif_stop_queue(dev);
> +
> +	if ((--priv->hw->open_count) == 0) {
> +		if (netif_running(priv->hw->netdev))
> +			dev_close(priv->hw->netdev);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static int ieee802154_slave_ioctl(struct net_device *dev, struct ifreq *ifr,
> +		int cmd)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	struct sockaddr_ieee802154 *sa =
> +		(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
> +	int err = -ENOIOCTLCMD;
> +
> +	read_lock(&priv->mib_lock);
> +
> +	switch (cmd) {
> +	case SIOCGIFADDR:
> +		if (priv->pan_id == IEEE802154_PANID_BROADCAST ||
> +		    priv->short_addr == IEEE802154_ADDR_BROADCAST) {
> +			err = -EADDRNOTAVAIL;
> +			break;
> +		}
> +
> +		sa->family = AF_IEEE802154;
> +		sa->addr.addr_type = IEEE802154_ADDR_SHORT;
> +		sa->addr.pan_id = priv->pan_id;
> +		sa->addr.short_addr = priv->short_addr;
> +
> +		err = 0;
> +		break;
> +	case SIOCSIFADDR:
> +		dev_warn(&dev->dev,
> +			"Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n");
> +		if (sa->family != AF_IEEE802154 ||
> +		    sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
> +		    sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
> +		    sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
> +		    sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
> +			err = -EINVAL;
> +			break;
> +		}
> +
> +		priv->pan_id = sa->addr.pan_id;
> +		priv->short_addr = sa->addr.short_addr;
> +		err = 0;
> +		break;
> +	}
> +	read_unlock(&priv->mib_lock);
> +	return err;
> +}
> +
> +static int ieee802154_slave_mac_addr(struct net_device *dev, void *p)
> +{
> +	struct sockaddr *addr = p;
> +
> +	if (netif_running(dev))
> +		return -EBUSY;
> +	/* FIXME: validate addr */
> +	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
> +	return 0;
> +}
> +
> +static void ieee802154_haddr_copy_swap(u8 *dest, const u8 *src)
> +{
> +	int i;
> +	for (i = 0; i < IEEE802154_ADDR_LEN; i++)
> +		dest[IEEE802154_ADDR_LEN - i - 1] = src[i];
> +}
> +
> +static int ieee802154_header_create(struct sk_buff *skb,
> +			   struct net_device *dev,
> +			   unsigned short type, const void *_daddr,
> +			   const void *_saddr, unsigned len)
> +{
> +	u8 head[24] = {};
> +	int pos = 0;
> +
> +	u16 fc;
> +	const struct ieee802154_addr *saddr = _saddr;
> +	const struct ieee802154_addr *daddr = _daddr;
> +	struct ieee802154_addr dev_addr;
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	fc = mac_cb_type(skb);
> +	if (mac_cb_is_ackreq(skb))
> +		fc |= IEEE802154_FC_ACK_REQ;
> +
> +	pos = 2;
> +
> +	head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */
> +
> +	if (!daddr)
> +		return -EINVAL;
> +
> +	if (!saddr) {
> +		read_lock(&priv->mib_lock);
> +		if (priv->short_addr == IEEE802154_ADDR_BROADCAST ||
> +		    priv->short_addr == IEEE802154_ADDR_UNDEF ||
> +		    priv->pan_id == IEEE802154_PANID_BROADCAST) {
> +			dev_addr.addr_type = IEEE802154_ADDR_LONG;
> +			memcpy(dev_addr.hwaddr, dev->dev_addr,
> +					IEEE802154_ADDR_LEN);
> +		} else {
> +			dev_addr.addr_type = IEEE802154_ADDR_SHORT;
> +			dev_addr.short_addr = priv->short_addr;
> +		}
> +
> +		dev_addr.pan_id = priv->pan_id;
> +		saddr = &dev_addr;
> +
> +		read_unlock(&priv->mib_lock);
> +	}
> +
> +	if (daddr->addr_type != IEEE802154_ADDR_NONE) {
> +		fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT);
> +
> +		head[pos++] = daddr->pan_id & 0xff;
> +		head[pos++] = daddr->pan_id >> 8;
> +
> +		if (daddr->addr_type == IEEE802154_ADDR_SHORT) {
> +			head[pos++] = daddr->short_addr & 0xff;
> +			head[pos++] = daddr->short_addr >> 8;
> +		} else {
> +			ieee802154_haddr_copy_swap(head + pos, daddr->hwaddr);
> +			pos += IEEE802154_ADDR_LEN;
> +		}
> +	}
> +
> +	if (saddr->addr_type != IEEE802154_ADDR_NONE) {
> +		fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT);
> +
> +		if ((saddr->pan_id == daddr->pan_id) &&
> +		    (saddr->pan_id != IEEE802154_PANID_BROADCAST))
> +			/* PANID compression/ intra PAN */
> +			fc |= IEEE802154_FC_INTRA_PAN;
> +		else {
> +			head[pos++] = saddr->pan_id & 0xff;
> +			head[pos++] = saddr->pan_id >> 8;
> +		}
> +
> +		if (saddr->addr_type == IEEE802154_ADDR_SHORT) {
> +			head[pos++] = saddr->short_addr & 0xff;
> +			head[pos++] = saddr->short_addr >> 8;
> +		} else {
> +			ieee802154_haddr_copy_swap(head + pos, saddr->hwaddr);
> +			pos += IEEE802154_ADDR_LEN;
> +		}
> +	}
> +
> +	head[0] = fc;
> +	head[1] = fc >> 8;
> +
> +	memcpy(skb_push(skb, pos), head, pos);
> +
> +	return pos;
> +}
> +
> +static int ieee802154_header_parse(const struct sk_buff *skb,
> +		unsigned char *haddr)
> +{
> +	const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb);
> +	struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
> +	u16 fc;
> +	int da_type;
> +
> +	if (hdr + 3 > tail)
> +		goto malformed;
> +
> +	fc = hdr[0] | (hdr[1] << 8);
> +
> +	hdr += 3;
> +
> +	da_type = IEEE802154_FC_DAMODE(fc);
> +	addr->addr_type = IEEE802154_FC_SAMODE(fc);
> +
> +	switch (da_type) {
> +	case IEEE802154_ADDR_NONE:
> +		if (fc & IEEE802154_FC_INTRA_PAN)
> +			goto malformed;
> +		break;
> +
> +	case IEEE802154_ADDR_LONG:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (fc & IEEE802154_FC_INTRA_PAN) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + IEEE802154_ADDR_LEN > tail)
> +			goto malformed;
> +		hdr += IEEE802154_ADDR_LEN;
> +		break;
> +
> +	case IEEE802154_ADDR_SHORT:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (fc & IEEE802154_FC_INTRA_PAN) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		hdr += 2;
> +		break;
> +
> +	default:
> +		goto malformed;
> +
> +	}
> +
> +	switch (addr->addr_type) {
> +	case IEEE802154_ADDR_NONE:
> +		break;
> +
> +	case IEEE802154_ADDR_LONG:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (!(fc & IEEE802154_FC_INTRA_PAN)) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + IEEE802154_ADDR_LEN > tail)
> +			goto malformed;
> +		memcpy(addr->hwaddr, hdr, IEEE802154_ADDR_LEN);
> +		hdr += IEEE802154_ADDR_LEN;
> +		break;
> +
> +	case IEEE802154_ADDR_SHORT:
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		if (!(fc & IEEE802154_FC_INTRA_PAN)) {
> +			addr->pan_id = hdr[0] | (hdr[1] << 8);
> +			hdr += 2;
> +		}
> +
> +		if (hdr + 2 > tail)
> +			goto malformed;
> +		addr->short_addr = hdr[0] | (hdr[1] << 8);
> +		hdr += 2;
> +		break;
> +
> +	default:
> +		goto malformed;
> +
> +	}
> +
> +	return sizeof(struct ieee802154_addr);
> +
> +malformed:
> +	pr_debug("malformed packet\n");
> +	return 0;
> +}
> +
> +static struct header_ops ieee802154_header_ops = {
> +	.create		= ieee802154_header_create,
> +	.parse		= ieee802154_header_parse,
> +};
> +
> +static const struct net_device_ops ieee802154_slave_ops = {
> +	.ndo_open		= ieee802154_slave_open,
> +	.ndo_stop		= ieee802154_slave_close,
> +	.ndo_start_xmit		= ieee802154_net_xmit,
> +	.ndo_do_ioctl		= ieee802154_slave_ioctl,
> +	.ndo_set_mac_address	= ieee802154_slave_mac_addr,
> +};
> +
> +static void ieee802154_netdev_setup(struct net_device *dev)
> +{
> +	dev->addr_len		= IEEE802154_ADDR_LEN;
> +	memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
> +	dev->features		= NETIF_F_NO_CSUM;
> +	dev->hard_header_len	= 2 + 1 + 20 + 14;
> +	dev->header_ops		= &ieee802154_header_ops;
> +	dev->needed_tailroom	= 2; /* FCS */
> +	dev->mtu		= 127;
> +	dev->tx_queue_len	= 10;
> +	dev->type		= ARPHRD_IEEE802154;
> +	dev->flags		= IFF_NOARP | IFF_BROADCAST;
> +	dev->watchdog_timeo	= 0;
> +
> +	dev->destructor		= free_netdev;
> +	dev->netdev_ops		= &ieee802154_slave_ops;
> +	dev->ml_priv		= &mac802154_mlme;
> +}
> +
> +/*
> + * This is for hw unregistration only, as it doesn't do RCU locking

So this list is different than the RCU-protected one, and readers
always hold locks when traversing it?

I am not familiar with this code, so might be missing something, but
it looks to me like the same list that RCU readers traverse.  If so,
need list_del_rcu() and synchronize_rcu().

> + */
> +void ieee802154_drop_slaves(struct ieee802154_dev *hw)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +	struct ieee802154_sub_if_data *sdata, *next;
> +
> +	ASSERT_RTNL();
> +
> +	list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
> +		mutex_lock(&sdata->hw->slaves_mtx);
> +		list_del(&sdata->list);
> +		mutex_unlock(&sdata->hw->slaves_mtx);
> +
> +		dev_put(sdata->hw->netdev);
> +
> +		unregister_netdevice(sdata->dev);
> +	}
> +}
> +
> +static int ieee802154_netdev_validate(struct nlattr *tb[],
> +		struct nlattr *data[])
> +{
> +	if (tb[IFLA_ADDRESS])
> +		if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
> +			return -EINVAL;
> +
> +	if (tb[IFLA_BROADCAST])
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int ieee802154_netdev_newlink(struct net_device *dev,
> +					   struct nlattr *tb[],
> +					   struct nlattr *data[])
> +{
> +	struct net_device *mdev;
> +	struct ieee802154_sub_if_data *priv;
> +	struct ieee802154_priv *ipriv;
> +	int err;
> +
> +	if (!tb[IFLA_LINK])
> +		return -EOPNOTSUPP;
> +
> +	mdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
> +	if (!mdev)
> +		return -ENODEV;
> +
> +	if (mdev->type != ARPHRD_IEEE802154_PHY)
> +		return -EINVAL;
> +
> +	ipriv = netdev_priv(mdev);
> +
> +	priv = netdev_priv(dev);
> +	priv->dev = dev;
> +	priv->hw = ipriv;
> +
> +	rwlock_init(&priv->mib_lock);
> +
> +	get_random_bytes(&priv->bsn, 1);
> +	get_random_bytes(&priv->dsn, 1);
> +
> +	priv->pan_id = IEEE802154_PANID_BROADCAST;
> +	priv->short_addr = IEEE802154_ADDR_BROADCAST;
> +
> +	dev_hold(ipriv->netdev);
> +
> +	dev->needed_headroom = ipriv->hw.extra_tx_headroom;
> +
> +	SET_NETDEV_DEV(dev, &ipriv->netdev->dev);
> +
> +	err = register_netdevice(dev);
> +	if (err < 0)
> +		return err;
> +
> +	mutex_lock(&ipriv->slaves_mtx);
> +	list_add_tail_rcu(&priv->list, &ipriv->slaves);
> +	mutex_unlock(&ipriv->slaves_mtx);
> +
> +	return 0;
> +}
> +
> +static void ieee802154_netdev_dellink(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *sdata;
> +	ASSERT_RTNL();
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	sdata = netdev_priv(dev);
> +
> +	mutex_lock(&sdata->hw->slaves_mtx);
> +	list_del_rcu(&sdata->list);
> +	mutex_unlock(&sdata->hw->slaves_mtx);
> +
> +	dev_put(sdata->hw->netdev);
> +
> +	synchronize_rcu();
> +	unregister_netdevice(sdata->dev);
> +}
> +
> +static size_t ieee802154_netdev_get_size(const struct net_device *dev)
> +{
> +	return	nla_total_size(2) +	/* IFLA_WPAN_CHANNEL */
> +		nla_total_size(2) +	/* IFLA_WPAN_PAN_ID */
> +		nla_total_size(2) +	/* IFLA_WPAN_SHORT_ADDR */
> +		nla_total_size(2) +	/* IFLA_WPAN_COORD_SHORT_ADDR */
> +		nla_total_size(8);	/* IFLA_WPAN_COORD_EXT_ADDR */
> +}
> +
> +static int ieee802154_netdev_fill_info(struct sk_buff *skb,
> +					const struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	read_lock(&priv->mib_lock);
> +
> +	NLA_PUT_U16(skb, IFLA_WPAN_CHANNEL, priv->chan);
> +	NLA_PUT_U16(skb, IFLA_WPAN_PAN_ID, priv->pan_id);
> +	NLA_PUT_U16(skb, IFLA_WPAN_SHORT_ADDR, priv->short_addr);
> +	/* TODO: IFLA_WPAN_COORD_SHORT_ADDR */
> +	/* TODO: IFLA_WPAN_COORD_EXT_ADDR */
> +
> +	read_unlock(&priv->mib_lock);
> +
> +	return 0;
> +
> +nla_put_failure:
> +	read_unlock(&priv->mib_lock);
> +	return -EMSGSIZE;
> +}
> +
> +static struct rtnl_link_ops wpan_link_ops __read_mostly = {
> +	.kind		= "wpan",
> +	.priv_size	= sizeof(struct ieee802154_sub_if_data),
> +	.setup		= ieee802154_netdev_setup,
> +	.validate	= ieee802154_netdev_validate,
> +	.newlink	= ieee802154_netdev_newlink,
> +	.dellink	= ieee802154_netdev_dellink,
> +	.get_size	= ieee802154_netdev_get_size,
> +	.fill_info	= ieee802154_netdev_fill_info,
> +};
> +
> +static int ieee802154_process_beacon(struct net_device *dev,
> +		struct sk_buff *skb)
> +{
> +	pr_warning("ieee802154: beacon frames are not yet supported\n");
> +	kfree_skb(skb);
> +	return NET_RX_DROP;
> +}
> +
> +static int ieee802154_process_ack(struct net_device *dev, struct sk_buff *skb)
> +{
> +	pr_debug("got ACK for SEQ=%d\n", mac_cb(skb)->seq);
> +
> +	kfree_skb(skb);
> +	return NET_RX_SUCCESS;
> +}
> +
> +static int ieee802154_process_data(struct net_device *dev, struct sk_buff *skb)
> +{
> +	return netif_rx(skb);
> +}
> +
> +static int ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
> +		struct sk_buff *skb)
> +{
> +	pr_debug("%s Getting packet via slave interface %s\n",
> +				__func__, sdata->dev->name);
> +
> +	read_lock(&sdata->mib_lock);
> +
> +	switch (mac_cb(skb)->da.addr_type) {
> +	case IEEE802154_ADDR_NONE:
> +		if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE)
> +			/* FIXME: check if we are PAN coordinator :) */
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else
> +			/* ACK comes with both addresses empty */
> +			skb->pkt_type = PACKET_HOST;
> +		break;
> +	case IEEE802154_ADDR_LONG:
> +		if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
> +		    mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr,
> +					IEEE802154_ADDR_LEN))
> +			skb->pkt_type = PACKET_HOST;
> +		else
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		break;
> +	case IEEE802154_ADDR_SHORT:
> +		if (mac_cb(skb)->da.pan_id != sdata->pan_id &&
> +		    mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST)
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		else if (mac_cb(skb)->da.short_addr == sdata->short_addr)
> +			skb->pkt_type = PACKET_HOST;
> +		else if (mac_cb(skb)->da.short_addr ==
> +					IEEE802154_ADDR_BROADCAST)
> +			skb->pkt_type = PACKET_BROADCAST;
> +		else
> +			skb->pkt_type = PACKET_OTHERHOST;
> +		break;
> +	}
> +
> +	read_unlock(&sdata->mib_lock);
> +
> +	skb->dev = sdata->dev;
> +
> +	if (skb->pkt_type == PACKET_HOST && mac_cb_is_ackreq(skb) &&
> +			!(sdata->hw->hw.flags & IEEE802154_HW_AACK))
> +		dev_warn(&sdata->dev->dev,
> +			"ACK requested, however AACK not supported.\n");
> +
> +	switch (mac_cb_type(skb)) {
> +	case IEEE802154_FC_TYPE_BEACON:
> +		return ieee802154_process_beacon(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_ACK:
> +		return ieee802154_process_ack(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_MAC_CMD:
> +		return ieee802154_process_cmd(sdata->dev, skb);
> +	case IEEE802154_FC_TYPE_DATA:
> +		return ieee802154_process_data(sdata->dev, skb);
> +	default:
> +		pr_warning("ieee802154: Bad frame received (type = %d)\n",
> +				mac_cb_type(skb));
> +		kfree_skb(skb);
> +		return NET_RX_DROP;
> +	}
> +}
> +
> +static u8 fetch_skb_u8(struct sk_buff *skb)
> +{
> +	u8 ret;
> +
> +	BUG_ON(skb->len < 1);
> +
> +	ret = skb->data[0];
> +	skb_pull(skb, 1);
> +
> +	return ret;
> +}
> +
> +static u16 fetch_skb_u16(struct sk_buff *skb)
> +{
> +	u16 ret;
> +
> +	BUG_ON(skb->len < 2);
> +
> +	ret = skb->data[0] + (skb->data[1] * 256);
> +	skb_pull(skb, 2);
> +	return ret;
> +}
> +
> +static void fetch_skb_u64(struct sk_buff *skb, void *data)
> +{
> +	BUG_ON(skb->len < IEEE802154_ADDR_LEN);
> +
> +	memcpy(data, skb->data, IEEE802154_ADDR_LEN);
> +	skb_pull(skb, IEEE802154_ADDR_LEN);
> +}
> +
> +#define IEEE802154_FETCH_U8(skb, var)		\
> +	do {					\
> +		if (skb->len < 1)		\
> +			goto exit_error;	\
> +		var = fetch_skb_u8(skb);	\
> +	} while (0)
> +
> +#define IEEE802154_FETCH_U16(skb, var)		\
> +	do {					\
> +		if (skb->len < 2)		\
> +			goto exit_error;	\
> +		var = fetch_skb_u16(skb);	\
> +	} while (0)
> +
> +#define IEEE802154_FETCH_U64(skb, var)			\
> +	do {						\
> +		if (skb->len < IEEE802154_ADDR_LEN)	\
> +			goto exit_error;		\
> +		fetch_skb_u64(skb, &var);		\
> +	} while (0)
> +
> +static int parse_frame_start(struct sk_buff *skb)
> +{
> +	u8 *head = skb->data;
> +	u16 fc;
> +
> +	if (skb->len < 3) {
> +		pr_debug("frame size %d bytes is too short\n", skb->len);
> +		return -EINVAL;
> +	}
> +
> +	IEEE802154_FETCH_U16(skb, fc);
> +	IEEE802154_FETCH_U8(skb, mac_cb(skb)->seq);
> +
> +	pr_debug("%s: %04x dsn%02x\n", __func__, fc, head[2]);
> +
> +	mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc);
> +
> +	if (fc & IEEE802154_FC_ACK_REQ) {
> +		pr_debug("%s(): ACKNOWLEDGE required\n", __func__);
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
> +	}
> +
> +	if (fc & IEEE802154_FC_SECEN)
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
> +
> +	if (fc & IEEE802154_FC_INTRA_PAN)
> +		mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN;
> +
> +	/* TODO */
> +	if (mac_cb_is_secen(skb)) {
> +		pr_info("security support is not implemented\n");
> +		return -EINVAL;
> +	}
> +
> +	mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc);
> +	if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_NONE)
> +		pr_debug("%s(): src addr_type is NONE\n", __func__);
> +
> +	mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc);
> +	if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_NONE)
> +		pr_debug("%s(): dst addr_type is NONE\n", __func__);
> +
> +	if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) {
> +		/* ACK can only have NONE-type addresses */
> +		if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE ||
> +		    mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE)
> +			return -EINVAL;
> +	}
> +
> +	if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) {
> +		IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.pan_id);
> +
> +		if (mac_cb_is_intrapan(skb)) { /* ! panid compress */
> +			pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
> +					__func__);
> +			mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id;
> +			pr_debug("%s(): src PAN address %04x\n",
> +					__func__, mac_cb(skb)->sa.pan_id);
> +		}
> +
> +		pr_debug("%s(): dst PAN address %04x\n",
> +				__func__, mac_cb(skb)->da.pan_id);
> +
> +		if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) {
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.short_addr);
> +			pr_debug("%s(): dst SHORT address %04x\n",
> +					__func__, mac_cb(skb)->da.short_addr);
> +
> +		} else {
> +			IEEE802154_FETCH_U64(skb, mac_cb(skb)->da.hwaddr);
> +			pr_debug("%s(): dst hardware addr\n", __func__);
> +		}
> +	}
> +
> +	if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) {
> +		pr_debug("%s(): got src non-NONE address\n", __func__);
> +		if (!(mac_cb_is_intrapan(skb))) { /* ! panid compress */
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.pan_id);
> +			pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n",
> +					__func__);
> +		}
> +
> +		if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) {
> +			IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.short_addr);
> +			pr_debug("%s(): src IEEE802154_ADDR_SHORT\n",
> +					__func__);
> +		} else {
> +			IEEE802154_FETCH_U64(skb, mac_cb(skb)->sa.hwaddr);
> +			pr_debug("%s(): src hardware addr\n", __func__);
> +		}
> +	}
> +
> +	return 0;
> +
> +exit_error:
> +	return -EINVAL;
> +}
> +
> +void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +	struct ieee802154_sub_if_data *sdata, *prev = NULL;
> +	int ret;
> +
> +	BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
> +	pr_debug("%s()\n", __func__);
> +
> +	if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
> +		u16 crc;
> +
> +		if (skb->len < 2) {
> +			pr_debug("%s(): Got invalid frame\n", __func__);
> +			goto out;
> +		}
> +		crc = crc_ccitt(0, skb->data, skb->len);
> +		if (crc) {
> +			pr_debug("%s(): CRC mismatch\n", __func__);
> +			goto out;
> +		}
> +		skb_trim(skb, skb->len - 2); /* CRC */
> +	}
> +
> +	ret = parse_frame_start(skb); /* 3 bytes pulled after this */
> +	if (ret) {
> +		pr_debug("%s(): Got invalid frame\n", __func__);
> +		goto out;
> +	}
> +
> +	pr_debug("%s() frame %d\n", __func__, mac_cb_type(skb));
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sdata, &priv->slaves, list)
> +	{
> +		if (prev) {
> +			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
> +			if (skb2)
> +				ieee802154_subif_frame(prev, skb2);
> +		}
> +
> +		prev = sdata;
> +	}
> +
> +	if (prev) {
> +		ieee802154_subif_frame(prev, skb);
> +		skb = NULL;
> +	}
> +
> +	rcu_read_unlock();
> +
> +out:
> +	dev_kfree_skb(skb);
> +	return;
> +}
> +
> +u16 ieee802154_dev_get_pan_id(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	read_lock(&priv->mib_lock);
> +	ret = priv->pan_id;
> +	read_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +u16 ieee802154_dev_get_short_addr(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	read_lock(&priv->mib_lock);
> +	ret = priv->short_addr;
> +	read_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->pan_id = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->short_addr = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +void ieee802154_dev_set_channel(struct net_device *dev, u8 val)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	priv->chan = val;
> +	write_unlock(&priv->mib_lock);
> +}
> +
> +u8 ieee802154_dev_get_dsn(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	ret = priv->dsn++;
> +	write_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +u8 ieee802154_dev_get_bsn(struct net_device *dev)
> +{
> +	struct ieee802154_sub_if_data *priv = netdev_priv(dev);
> +	u16 ret;
> +
> +	BUG_ON(dev->type != ARPHRD_IEEE802154);
> +
> +	write_lock(&priv->mib_lock);
> +	ret = priv->bsn++;
> +	write_unlock(&priv->mib_lock);
> +
> +	return ret;
> +}
> +
> +static int __init ieee802154_dev_init(void)
> +{
> +	return rtnl_link_register(&wpan_link_ops);
> +}
> +module_init(ieee802154_dev_init);
> +
> +static void __exit ieee802154_dev_exit(void)
> +{
> +	rtnl_link_unregister(&wpan_link_ops);
> +}
> +module_exit(ieee802154_dev_exit);
> +
> +MODULE_ALIAS_RTNL_LINK("wpan");
> +
> diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h
> new file mode 100644
> index 0000000..f4e8d29
> --- /dev/null
> +++ b/net/mac802154/mac802154.h
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +#ifndef MAC802154_H
> +#define MAC802154_H
> +
> +#include <linux/spinlock.h>
> +struct ieee802154_priv {
> +	struct ieee802154_dev	hw;
> +	struct ieee802154_ops	*ops;
> +
> +	struct net_device *netdev; /* mwpanX device */
> +	int open_count;
> +	/* As in mac80211 slaves list is modified:
> +	 * 1) under the RTNL
> +	 * 2) protected by slaves_mtx;
> +	 * 3) in an RCU manner
> +	 *
> +	 * So atomic readers can use any of this protection methods
> +	 */
> +	struct list_head	slaves;
> +	struct mutex		slaves_mtx;
> +	/* This one is used for scanning and other
> +	 * jobs not to be interfered with serial driver */
> +	struct workqueue_struct	*dev_workqueue;
> +};
> +
> +#define ieee802154_to_priv(_hw)	container_of(_hw, struct ieee802154_priv, hw)
> +
> +struct ieee802154_sub_if_data {
> +	struct list_head list; /* the ieee802154_priv->slaves list */
> +
> +	struct ieee802154_priv *hw;
> +	struct net_device *dev;
> +
> +	rwlock_t mib_lock;
> +
> +	u16 pan_id;
> +	u16 short_addr;
> +
> +	u8 chan;
> +
> +	/* MAC BSN field */
> +	u8 bsn;
> +	/* MAC BSN field */
> +	u8 dsn;
> +};
> +
> +void ieee802154_drop_slaves(struct ieee802154_dev *hw);
> +
> +void ieee802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb);
> +
> +struct ieee802154_phy_cb {
> +	u8 lqi;
> +	u8 chan;
> +};
> +
> +static inline struct ieee802154_phy_cb *phy_cb(struct sk_buff *skb)
> +{
> +	return (struct ieee802154_phy_cb *)skb->cb;
> +}
> +
> +extern struct ieee802154_mlme_ops mac802154_mlme;
> +
> +int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb);
> +
> +#endif
> diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
> new file mode 100644
> index 0000000..8941628
> --- /dev/null
> +++ b/net/mac802154/mac_cmd.c
> @@ -0,0 +1,99 @@
> +/*
> + * MAC commands interface
> + *
> + * Copyright 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Sergey Lapin <slapin@ossfans.org>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/skbuff.h>
> +#include <linux/if_arp.h>
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +#include <net/ieee802154.h>
> +#include <net/ieee802154_netdev.h>
> +#include <net/nl802154.h>
> +
> +#include "mac802154.h"
> +#include "mib.h"
> +
> +int ieee802154_process_cmd(struct net_device *dev, struct sk_buff *skb)
> +{
> +	pr_warning("ieee802154: command frames are not yet supported\n");
> +	kfree_skb(skb);
> +	return NET_RX_DROP;
> +}
> +
> +
> +static int ieee802154_mlme_assoc_req(struct net_device *dev,
> +		struct ieee802154_addr *addr, u8 channel, u8 cap)
> +{
> +	/* We simply emulate it here */
> +	return ieee802154_nl_assoc_confirm(dev,
> +			ieee802154_dev_get_short_addr(dev),
> +			IEEE802154_SUCCESS);
> +}
> +
> +static int ieee802154_mlme_assoc_resp(struct net_device *dev,
> +		struct ieee802154_addr *addr, u16 short_addr, u8 status)
> +{
> +	return 0;
> +}
> +
> +static int ieee802154_mlme_disassoc_req(struct net_device *dev,
> +		struct ieee802154_addr *addr, u8 reason)
> +{
> +	return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
> +}
> +
> +static int ieee802154_mlme_start_req(struct net_device *dev,
> +				struct ieee802154_addr *addr,
> +				u8 channel,
> +				u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
> +				u8 coord_realign)
> +{
> +	/* We don't emulate beacons here at all, so START should fail */
> +	ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
> +	return 0;
> +}
> +
> +static int ieee802154_mlme_scan_req(struct net_device *dev, u8 type,
> +		u32 channels,
> +		u8 duration)
> +{
> +	u8 edl[27] = {};
> +	return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
> +			channels,
> +			type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
> +}
> +
> +
> +struct ieee802154_mlme_ops mac802154_mlme = {
> +	.assoc_req = ieee802154_mlme_assoc_req,
> +	.assoc_resp = ieee802154_mlme_assoc_resp,
> +	.disassoc_req = ieee802154_mlme_disassoc_req,
> +	.start_req = ieee802154_mlme_start_req,
> +	.scan_req = ieee802154_mlme_scan_req,
> +
> +	.get_pan_id = ieee802154_dev_get_pan_id,
> +	.get_short_addr = ieee802154_dev_get_short_addr,
> +	.get_dsn = ieee802154_dev_get_dsn,
> +	.get_bsn = ieee802154_dev_get_bsn,
> +};
> +
> diff --git a/net/mac802154/mdev.c b/net/mac802154/mdev.c
> new file mode 100644
> index 0000000..191e942
> --- /dev/null
> +++ b/net/mac802154/mdev.c
> @@ -0,0 +1,295 @@
> +/*
> + * Copyright (C) 2007, 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/netdevice.h>
> +#include <linux/if_arp.h>
> +#include <net/route.h>
> +
> +#include <net/af_ieee802154.h>
> +#include <net/mac802154.h>
> +
> +#include "mac802154.h"
> +
> +struct xmit_work {
> +	struct sk_buff *skb;
> +	struct work_struct work;
> +	struct ieee802154_priv *priv;
> +};
> +
> +static void ieee802154_xmit_worker(struct work_struct *work)
> +{
> +	struct xmit_work *xw = container_of(work, struct xmit_work, work);
> +	int res;
> +
> +	if (xw->priv->hw.current_channel != phy_cb(xw->skb)->chan) {
> +		res = xw->priv->ops->set_channel(&xw->priv->hw,
> +				phy_cb(xw->skb)->chan);
> +		if (res) {
> +			pr_debug("set_channel failed\n");
> +			goto out;
> +		}
> +	}
> +
> +	res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
> +
> +out:
> +	/* FIXME: result processing and/or requeue!!! */
> +	dev_kfree_skb(xw->skb);
> +
> +	kfree(xw);
> +}
> +
> +static int ieee802154_master_hard_start_xmit(struct sk_buff *skb,
> +		struct net_device *dev)
> +{
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +	struct xmit_work *work;
> +
> +	if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
> +		dev_kfree_skb(skb);
> +		return NETDEV_TX_OK;
> +	}
> +
> +	work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
> +	if (!work)
> +		return NETDEV_TX_BUSY;
> +
> +	INIT_WORK(&work->work, ieee802154_xmit_worker);
> +	work->skb = skb;
> +	work->priv = priv;
> +
> +	queue_work(priv->dev_workqueue, &work->work);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +static int ieee802154_master_open(struct net_device *dev)
> +{
> +	int rc;
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +
> +	if (!priv) {
> +		pr_debug("%s:%s: unable to get master private data\n",
> +				__FILE__, __func__);
> +		return -ENODEV;
> +	}
> +
> +	if (!priv->open_count)
> +		return -EOPNOTSUPP;
> +
> +	rc = priv->ops->start(&priv->hw);
> +
> +	if (!rc)
> +		netif_tx_start_all_queues(dev);
> +
> +	return rc;
> +}
> +
> +static int ieee802154_master_close(struct net_device *dev)
> +{
> +	struct ieee802154_priv *priv = netdev_priv(dev);
> +	struct ieee802154_sub_if_data *sdata;
> +
> +	ASSERT_RTNL();
> +
> +	/* We are under RTNL, so it's fine to do this */
> +	list_for_each_entry(sdata, &priv->slaves, list)
> +		if (netif_running(sdata->dev))
> +			dev_close(sdata->dev);
> +
> +	priv->ops->stop(&priv->hw);
> +
> +	return 0;
> +}
> +
> +static ssize_t ieee802154_netdev_show(const struct device *dev,
> +		   struct device_attribute *attr, char *buf,
> +		   ssize_t (*format)(const struct net_device *, char *))
> +{
> +	struct net_device *netdev = to_net_dev(dev);
> +	ssize_t ret = -EINVAL;
> +
> +	if (netdev->reg_state <= NETREG_REGISTERED)
> +		ret = (*format)(netdev, buf);
> +
> +	return ret;
> +}
> +#define MASTER_SHOW(field, format_string)				\
> +static ssize_t format_##field(const struct net_device *dev, char *buf)	\
> +{									\
> +	struct ieee802154_priv *priv = netdev_priv(dev);		\
> +	return sprintf(buf, format_string, priv->hw.field);		\
> +}									\
> +static ssize_t show_##field(struct device *dev,				\
> +			    struct device_attribute *attr, char *buf)	\
> +{									\
> +	return ieee802154_netdev_show(dev, attr, buf, format_##field);	\
> +}									\
> +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL)
> +
> +static const char fmt_long_hex[] = "%#lx\n";
> +static const char fmt_hex[] = "%#x\n";
> +static const char fmt_dec[] = "%d\n";
> +
> +MASTER_SHOW(current_channel, fmt_dec);
> +MASTER_SHOW(channel_mask, fmt_hex);
> +
> +static struct attribute *pmib_attrs[] = {
> +	&dev_attr_current_channel.attr,
> +	&dev_attr_channel_mask.attr,
> +	NULL
> +};
> +
> +static struct attribute_group pmib_group = {
> +	.name  = "pib",
> +	.attrs  = pmib_attrs,
> +};
> +
> +static const struct net_device_ops ieee802154_master_ops = {
> +	.ndo_open		= ieee802154_master_open,
> +	.ndo_stop		= ieee802154_master_close,
> +	.ndo_start_xmit		= ieee802154_master_hard_start_xmit,
> +};
> +
> +static void ieee802154_netdev_setup_master(struct net_device *dev)
> +{
> +	dev->addr_len		= 0;
> +	dev->features		= NETIF_F_NO_CSUM;
> +	dev->hard_header_len	= 0;
> +	dev->mtu		= 127;
> +	dev->tx_queue_len	= 0;
> +	dev->type		= ARPHRD_IEEE802154_PHY;
> +	dev->flags		= IFF_NOARP | IFF_BROADCAST;
> +	dev->watchdog_timeo	= 0;
> +
> +	dev->netdev_ops = &ieee802154_master_ops;
> +}
> +
> +struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size,
> +		struct ieee802154_ops *ops)
> +{
> +	struct net_device *dev;
> +	struct ieee802154_priv *priv;
> +
> +	dev = alloc_netdev(ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_size,
> +			"mwpan%d", ieee802154_netdev_setup_master);
> +	if (!dev) {
> +		printk(KERN_ERR
> +			"Failure to initialize master IEEE802154 device\n");
> +		return NULL;
> +	}
> +	priv = netdev_priv(dev);
> +	priv->netdev = dev;
> +	priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
> +
> +	BUG_ON(!dev);
> +	BUG_ON(!ops);
> +	BUG_ON(!ops->xmit);
> +	BUG_ON(!ops->ed);
> +	BUG_ON(!ops->start);
> +	BUG_ON(!ops->stop);
> +
> +	if (!try_module_get(ops->owner)) {
> +		free_netdev(dev);
> +		return NULL;
> +	}
> +
> +	priv->ops = ops;
> +
> +	INIT_LIST_HEAD(&priv->slaves);
> +	mutex_init(&priv->slaves_mtx);
> +
> +	return &priv->hw;
> +}
> +EXPORT_SYMBOL(ieee802154_alloc_device);
> +
> +void ieee802154_free_device(struct ieee802154_dev *hw)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(hw);
> +
> +	BUG_ON(!list_empty(&priv->slaves));
> +	BUG_ON(!priv->netdev);
> +
> +	module_put(priv->ops->owner);
> +
> +	free_netdev(priv->netdev);
> +}
> +EXPORT_SYMBOL(ieee802154_free_device);
> +
> +int ieee802154_register_device(struct ieee802154_dev *dev)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +	struct net_device *ndev = priv->netdev;
> +
> +	int rc;
> +
> +	rtnl_lock();
> +	if (strchr(ndev->name, '%')) {
> +		rc = dev_alloc_name(ndev, ndev->name);
> +		if (rc < 0)
> +			goto out_unlock;
> +	}
> +
> +	priv->dev_workqueue =
> +		create_singlethread_workqueue(ndev->name);
> +	if (!priv->dev_workqueue) {
> +		rc = -ENOMEM;
> +		goto out_unlock;
> +	}
> +
> +	ndev->needed_headroom = priv->hw.extra_tx_headroom;
> +	SET_NETDEV_DEV(ndev, priv->hw.parent);
> +
> +	ndev->sysfs_groups[1] = &pmib_group;
> +
> +	rc = register_netdevice(ndev);
> +	if (rc < 0)
> +		goto out_wq;
> +
> +	rtnl_unlock();
> +
> +	return 0;
> +
> +out_wq:
> +	destroy_workqueue(priv->dev_workqueue);
> +out_unlock:
> +	rtnl_unlock();
> +	return rc;
> +}
> +EXPORT_SYMBOL(ieee802154_register_device);
> +
> +void ieee802154_unregister_device(struct ieee802154_dev *dev)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +
> +	flush_workqueue(priv->dev_workqueue);
> +	destroy_workqueue(priv->dev_workqueue);
> +
> +	rtnl_lock();
> +
> +	ieee802154_drop_slaves(dev);
> +	unregister_netdevice(priv->netdev);
> +
> +	rtnl_unlock();
> +}
> +EXPORT_SYMBOL(ieee802154_unregister_device);
> +
> +MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
> +MODULE_LICENSE("GPL v2");
> +
> diff --git a/net/mac802154/mib.h b/net/mac802154/mib.h
> new file mode 100644
> index 0000000..57bf03f
> --- /dev/null
> +++ b/net/mac802154/mib.h
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright 2008 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + */
> +
> +#ifndef MIB802154_H
> +#define MIB802154_H
> +
> +/* FIXME: should be dropped in favour of generic MIB API */
> +u8 ieee802154_dev_get_dsn(struct net_device *dev);
> +u8 ieee802154_dev_get_bsn(struct net_device *dev);
> +u16 ieee802154_dev_get_pan_id(struct net_device *dev);
> +u16 ieee802154_dev_get_short_addr(struct net_device *dev);
> +void ieee802154_dev_set_pan_id(struct net_device *dev, u16 val);
> +void ieee802154_dev_set_short_addr(struct net_device *dev, u16 val);
> +void ieee802154_dev_set_channel(struct net_device *dev, u8 chan);
> +
> +
> +#endif
> diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
> new file mode 100644
> index 0000000..81a269e
> --- /dev/null
> +++ b/net/mac802154/rx.c
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright (C) 2007, 2008, 2009 Siemens AG
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Written by:
> + * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
> + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/workqueue.h>
> +#include <linux/netdevice.h>
> +
> +#include <net/mac802154.h>
> +
> +#include "mac802154.h"
> +
> +static void __ieee802154_rx_prepare(struct ieee802154_dev *dev,
> +		struct sk_buff *skb, u8 lqi)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +
> +	BUG_ON(!skb);
> +
> +	phy_cb(skb)->lqi = lqi;
> +
> +	skb->dev = priv->netdev;
> +
> +	skb->iif = skb->dev->ifindex;
> +
> +	skb->protocol = htons(ETH_P_IEEE802154);
> +
> +	skb_reset_mac_header(skb);
> +}
> +
> +void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
> +{
> +	struct sk_buff *skb2;
> +
> +	__ieee802154_rx_prepare(dev, skb, lqi);
> +
> +	skb2 = skb_clone(skb, GFP_KERNEL);
> +	netif_rx(skb2);
> +
> +	ieee802154_subif_rx(dev, skb);
> +}
> +EXPORT_SYMBOL(ieee802154_rx);
> +
> +struct rx_work {
> +	struct sk_buff *skb;
> +	struct work_struct work;
> +	struct ieee802154_dev *dev;
> +};
> +
> +static void ieee802154_rx_worker(struct work_struct *work)
> +{
> +	struct rx_work *rw = container_of(work, struct rx_work, work);
> +	struct sk_buff *skb = rw->skb;
> +
> +	struct sk_buff *skb2 = skb_clone(skb, GFP_KERNEL);
> +	netif_rx(skb2);
> +
> +	ieee802154_subif_rx(rw->dev, skb);
> +	kfree(rw);
> +}
> +
> +void ieee802154_rx_irqsafe(struct ieee802154_dev *dev,
> +		struct sk_buff *skb, u8 lqi)
> +{
> +	struct ieee802154_priv *priv = ieee802154_to_priv(dev);
> +	struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
> +
> +	if (!work)
> +		return;
> +
> +	__ieee802154_rx_prepare(dev, skb, lqi);
> +
> +	INIT_WORK(&work->work, ieee802154_rx_worker);
> +	work->skb = skb;
> +	work->dev = dev;
> +
> +	queue_work(priv->dev_workqueue, &work->work);
> +}
> +EXPORT_SYMBOL(ieee802154_rx_irqsafe);
> -- 
> 1.6.3.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 2/2] ieee802154: add virtual loopback driver
From: Oliver Hartkopp @ 2009-08-10 17:28 UTC (permalink / raw)
  To: Dmitry Eremin-Solenikov
  Cc: netdev, linux-zigbee-devel, linux-wireless, Sergey Lapin,
	Patrick McHardy
In-Reply-To: <1249913800-10176-3-git-send-email-dbaryshkov@gmail.com>

Dmitry Eremin-Solenikov wrote:
> fakelb is a virtual loopback driver implementing one or several
> interconnected radios. 

> +
> +static int radios = 3;
> +module_param(radios, int, 0444);
> +MODULE_PARM_DESC(radios, "Number of simulated radios");
> +


> +
> +	for (i = 0; i < radios; i++) {
> +		err = ieee802154fake_add_priv(&pdev->dev, priv);
> +		if (err < 0)
> +			goto err_slave;
> +	}
> +

Hi Dimtry,

creating software networking devices like this is IMO not state-of-the-art for
 kernel inclusion.

Please use the NETLINK interface for creating the virtual loopback drivers,
like e.g. the virtual CAN driver (drivers/net/can/vcan.c) does.

Regards,
Oliver

^ permalink raw reply

* Re: Libertas: Association request to the driver failed
From: Daniel Mack @ 2009-08-10 17:47 UTC (permalink / raw)
  To: Roel Kluin
  Cc: Cyrill Gorcunov, Michael Buesch, John W. Linville, libertas-dev,
	linux-wireless, linux-kernel
In-Reply-To: <4A7FF84C.7070708@gmail.com>

On Mon, Aug 10, 2009 at 12:37:00PM +0200, Roel Kluin wrote:
> I think there was another problem in lbs_associate(),
> the memcpy already affected rates->rates.
> 
> Also in get_common_rates() I think we can safely move the
> memset/memcpy, originally after label done, upwards.
> 
> The patch below, if correct, is to be applied after the revert

I tested that and the driver still works fine for me. Thanks :)

Feel free to add my Tested-by: if you like.

Daniel


> diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
> index b9b3741..ba0164a 100644
> --- a/drivers/net/wireless/libertas/assoc.c
> +++ b/drivers/net/wireless/libertas/assoc.c
> @@ -1,6 +1,7 @@
>  /* Copyright (C) 2006, Red Hat, Inc. */
>  
>  #include <linux/types.h>
> +#include <linux/kernel.h>
>  #include <linux/etherdevice.h>
>  #include <linux/ieee80211.h>
>  #include <linux/if_arp.h>
> @@ -43,41 +44,41 @@ static int get_common_rates(struct lbs_private *priv,
>  	u16 *rates_size)
>  {
>  	u8 *card_rates = lbs_bg_rates;
> -	size_t num_card_rates = sizeof(lbs_bg_rates);
> -	int ret = 0, i, j;
> -	u8 tmp[30];
> +	int i, j;
> +	u8 tmp[MAX_RATES * ARRAY_SIZE(lbs_bg_rates)];
>  	size_t tmp_size = 0;
>  
>  	/* For each rate in card_rates that exists in rate1, copy to tmp */
> -	for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
> -		for (j = 0; rates[j] && (j < *rates_size); j++) {
> +	for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && card_rates[i]; i++) {
> +		for (j = 0; j < *rates_size && rates[j]; j++) {
>  			if (rates[j] == card_rates[i])
>  				tmp[tmp_size++] = card_rates[i];
>  		}
>  	}
>  
>  	lbs_deb_hex(LBS_DEB_JOIN, "AP rates    ", rates, *rates_size);
> -	lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates, num_card_rates);
> +	lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates,
> +			ARRAY_SIZE(lbs_bg_rates));
>  	lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
>  	lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
>  
> +	memset(rates, 0, *rates_size);
> +	*rates_size = min_t(u16, tmp_size, *rates_size);
> +	memcpy(rates, tmp, *rates_size);
> +
>  	if (!priv->enablehwauto) {
>  		for (i = 0; i < tmp_size; i++) {
>  			if (tmp[i] == priv->cur_rate)
> -				goto done;
> +				break;
> +		}
> +		if (i == tmp_size) {
> +			lbs_pr_alert("Previously set fixed data rate %#x isn't "
> +					"compatible with the network.\n",
> +					priv->cur_rate);
> +			return -1;
>  		}
> -		lbs_pr_alert("Previously set fixed data rate %#x isn't "
> -		       "compatible with the network.\n", priv->cur_rate);
> -		ret = -1;
> -		goto done;
>  	}
> -	ret = 0;
> -
> -done:
> -	memset(rates, 0, *rates_size);
> -	*rates_size = min_t(int, tmp_size, *rates_size);
> -	memcpy(rates, tmp, *rates_size);
> -	return ret;
> +	return 0;
>  }
>  
>  
> @@ -321,8 +322,8 @@ static int lbs_associate(struct lbs_private *priv,
>  
>  	rates = (struct mrvl_ie_rates_param_set *) pos;
>  	rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
> -	memcpy(&rates->rates, &bss->rates, MAX_RATES);
> -	tmplen = MAX_RATES;
> +	tmplen = min_t(u16, ARRAY_SIZE(rates->rates), MAX_RATES);
> +	memcpy(&rates->rates, &bss->rates, tmplen);
>  	if (get_common_rates(priv, rates->rates, &tmplen)) {
>  		ret = -1;
>  		goto done;
> @@ -598,7 +599,7 @@ static int lbs_adhoc_join(struct lbs_private *priv,
>  
>  	/* Copy Data rates from the rates recorded in scan response */
>  	memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates));
> -	ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES);
> +	ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), MAX_RATES);
>  	memcpy(cmd.bss.rates, bss->rates, ratesize);
>  	if (get_common_rates(priv, cmd.bss.rates, &ratesize)) {
>  		lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n");

^ permalink raw reply

* Re: Libertas: Association request to the driver failed
From: John W. Linville @ 2009-08-10 17:59 UTC (permalink / raw)
  To: Roel Kluin; +Cc: Daniel Mack, libertas-dev, linux-wireless, linux-kernel
In-Reply-To: <4A7EAED8.9090900@gmail.com>

On Sun, Aug 09, 2009 at 01:11:20PM +0200, Roel Kluin wrote:
> > I'll test that tomorrow. Would be easier if you send in a new patch I
> > can ack directly in case it works :)
> 
> This is the patch that should be applied after the revert, or do you want a
> delta patch?

Delta patch, please...

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* Re: Mesh fixes and improvements
From: John W. Linville @ 2009-08-10 17:52 UTC (permalink / raw)
  To: Javier Cardona; +Cc: Johannes Berg, linux-wireless, andrey, devel
In-Reply-To: <445f43ac0908082155n7cdde6e2p7656177fc2952be@mail.gmail.com>

On Sat, Aug 08, 2009 at 09:55:56PM -0700, Javier Cardona wrote:
> On Sat, Aug 8, 2009 at 2:09 AM, Johannes Berg<johannes@sipsolutions.net> wrote:
> >> Also, I'm not sure if "Simulate-transmission-losses-on-plinks-to-..." should be
> >> merged upstream, but we find it really useful to test mesh configurations.
> >> Please comment if you have different opinions on its adequacy or
> >> implementation.
> >
> > As I said elsewhere in this thread I don't think that should be in,
> > especially not, as Kalle points out, using nl80211.
> 
> Understood.  I'll withdraw that patch and regenerate the series...

Dropping the series based on the above comment...

-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* Re: [PATCH 2.6.31] ar9170: fix read & write outside array bounds
From: John W. Linville @ 2009-08-10 17:57 UTC (permalink / raw)
  To: Christian Lamparter; +Cc: wireless, Dan Carpenter
In-Reply-To: <200908091424.09712.chunkeey@web.de>

On Sun, Aug 09, 2009 at 02:24:09PM +0200, Christian Lamparter wrote:
> From: Dan Carpenter <error27@gmail.com>
> 
> queue == __AR9170_NUM_TXQ would cause a bug on the next line.
> 
> found by Smatch ( http://repo.or.cz/w/smatch.git ).
> 
> Cc: stable@kernel.org
> Reported-by: Dan Carpenter <error27@gmail.com>
> Signed-off-by: Dan Carpenter <error27@gmail.com>
> Signed-off-by: Christian Lamparter <chunkeey@web.de>
> ---
> diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
> index 4fc389a..ea8c941 100644
> --- a/drivers/net/wireless/ath/ar9170/main.c
> +++ b/drivers/net/wireless/ath/ar9170/main.c
> @@ -2458,13 +2458,14 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
>  	int ret;
>  
>  	mutex_lock(&ar->mutex);
> -	if ((param) && !(queue > __AR9170_NUM_TXQ)) {
> +	if (queue < __AR9170_NUM_TXQ) {
>  		memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
>  		       param, sizeof(*param));
>  
>  		ret = ar9170_set_qos(ar);
> -	} else
> +	} else {
>  		ret = -EINVAL;
> +	}
>  
>  	mutex_unlock(&ar->mutex);
>  	return ret;

The p54 version of this patch used hw->queues instead of a constant.
Wouldn't that be better here?

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* [PATCH] b43: Implement LP-PHY baseband table initialization
From: Gábor Stefanik @ 2009-08-10 18:39 UTC (permalink / raw)
  To: John Linville, Michael Buesch
  Cc: Larry Finger, Johannes Berg, Broadcom Wireless, linux-wireless

Implement LP-PHY baseband table init for all revisions.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
Changes from RFC:
-Improved table formatting in the code.
-The 2GHz check in adjust_gain_table now uses b43_current_band.
-Added a comment to rev2plus_table_init about a possible future improvement.

 drivers/net/wireless/b43/phy_lp.c       |   41 +-
 drivers/net/wireless/b43/tables_lpphy.c | 1716 +++++++++++++++++++++++++++++++
 drivers/net/wireless/b43/tables_lpphy.h |    3 +
 3 files changed, 1756 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index 27eadee..d2af924 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -59,9 +59,43 @@ static void b43_lpphy_op_free(struct b43_wldev *dev)
 	dev->phy.lp = NULL;
 }
 
+static void lpphy_adjust_gain_table(struct b43_wldev *dev)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	u32 freq = dev->wl->hw->conf.channel->center_freq;
+	u16 temp[3];
+	u16 isolation;
+
+	B43_WARN_ON(dev->phy.rev >= 2);
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+		isolation = lpphy->tx_isolation_med_band;
+	else if (freq <= 5320)
+		isolation = lpphy->tx_isolation_low_band;
+	else if (freq <= 5700)
+		isolation = lpphy->tx_isolation_med_band;
+	else
+		isolation = lpphy->tx_isolation_hi_band;
+
+	temp[0] = ((isolation - 26) / 12) << 12;
+	temp[1] = temp[0] + 0x1000;
+	temp[2] = temp[0] + 0x2000;
+
+	b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp);
+}
+
 static void lpphy_table_init(struct b43_wldev *dev)
 {
-	//TODO
+	if (dev->phy.rev < 2)
+		lpphy_rev0_1_table_init(dev);
+	else
+		lpphy_rev2plus_table_init(dev);
+
+	lpphy_init_tx_gain_table(dev);
+
+	if (dev->phy.rev < 2)
+		lpphy_adjust_gain_table(dev);
 }
 
 static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
@@ -596,13 +630,13 @@ static void lpphy_tx_pctl_init(struct b43_wldev *dev)
 static int b43_lpphy_op_init(struct b43_wldev *dev)
 {
 	/* TODO: band SPROM */
-	/* TODO: tables init */
 	lpphy_baseband_init(dev);
 	lpphy_radio_init(dev);
 	//TODO calibrate RC
 	//TODO set channel
 	lpphy_tx_pctl_init(dev);
-	//TODO full calib
+	lpphy_calibration(dev);
+	//TODO ACI init
 
 	return 0;
 }
@@ -680,7 +714,6 @@ static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev,
 	return B43_TXPWR_RES_DONE;
 }
 
-
 const struct b43_phy_operations b43_phyops_lp = {
 	.allocate		= b43_lpphy_op_allocate,
 	.free			= b43_lpphy_op_free,
diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/b43/tables_lpphy.c
index cadfe81..141e96d 100644
--- a/drivers/net/wireless/b43/tables_lpphy.c
+++ b/drivers/net/wireless/b43/tables_lpphy.c
@@ -710,3 +710,1719 @@ void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset,
 		offset++;
 	}
 }
+
+static const u8 lpphy_min_sig_sq_table[] = {
+	0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd,
+	0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+	0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd,
+	0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+};
+
+static const u16 lpphy_rev01_noise_scale_table[] = {
+	0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+	0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+	0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36,
+	0x0000, 0x0000, 0x4c00, 0x2d36,
+};
+
+static const u16 lpphy_rev2plus_noise_scale_table[] = {
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4,
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4,
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000,
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4,
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4,
+	0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4,
+	0x00a4,
+};
+
+static const u16 lpphy_crs_gain_nft_table[] = {
+	0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f,
+	0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381,
+	0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f,
+	0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d,
+};
+
+static const u16 lpphy_rev01_filter_control_table[] = {
+	0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53,
+	0x0127,
+};
+
+static const u32 lpphy_rev2plus_filter_control_table[] = {
+	0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f,
+	0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f,
+};
+
+static const u32 lpphy_rev01_ps_control_table[] = {
+	0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080,
+	0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0,
+	0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0,
+	0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0,
+	0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0,
+	0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0,
+	0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0,
+};
+
+static const u32 lpphy_rev2plus_ps_control_table[] = {
+	0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080,
+	0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043,
+	0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003,
+	0x00180006, 0x00080002,
+};
+
+static const u8 lpphy_pll_fraction_table[] = {
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80,
+	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+};
+
+static const u16 lpphy_iq_local_table[] = {
+	0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002,
+	0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600,
+	0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006,
+	0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000,
+};
+
+static const u16 lpphy_ofdm_cck_gain_table[] = {
+	0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001,
+	0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075,
+	0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d,
+	0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d,
+};
+
+static const u16 lpphy_gain_delta_table[] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 lpphy_tx_power_control_table[] = {
+	0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b,
+	0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045,
+	0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f,
+	0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039,
+	0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033,
+	0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d,
+	0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027,
+	0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021,
+	0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b,
+	0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015,
+	0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2,
+	0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1,
+	0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20,
+	0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23,
+	0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662,
+	0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61,
+	0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660,
+	0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663,
+	0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62,
+	0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661,
+	0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0,
+	0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3,
+	0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2,
+	0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61,
+	0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560,
+	0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563,
+	0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62,
+	0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1,
+	0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10,
+	0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13,
+	0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2,
+	0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000,
+	0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc,
+	0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff,
+	0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc,
+	0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03,
+	0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff,
+	0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500,
+	0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700,
+	0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404,
+	0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc,
+	0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd,
+	0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe,
+	0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe,
+	0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701,
+	0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc,
+	0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb,
+	0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05,
+	0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305,
+	0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701,
+	0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700,
+	0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb,
+	0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08,
+	0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702,
+};
+
+static const u32 lpphy_gain_idx_table[] = {
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000,
+	0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001,
+	0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000,
+	0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001,
+	0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011,
+	0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018,
+	0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019,
+	0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a,
+	0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a,
+	0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000,
+	0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001,
+	0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000,
+	0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000,
+	0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010,
+	0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018,
+	0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019,
+	0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019,
+	0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a,
+	0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a,
+};
+
+static const u16 lpphy_aux_gain_idx_table[] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016,
+};
+
+static const u32 lpphy_gain_value_table[] = {
+	0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004,
+	0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a,
+	0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006,
+	0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000,
+	0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f,
+	0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1,
+	0x00000000, 0x00000000,
+};
+
+static const u16 lpphy_gain_table[] = {
+	0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a,
+	0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816,
+	0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837,
+	0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db,
+	0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f,
+	0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 lpphy_a0_gain_idx_table[] = {
+	0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065,
+	0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f,
+	0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca,
+	0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e,
+	0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193,
+	0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a,
+	0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325,
+	0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471,
+	0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646,
+	0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd,
+	0x00036963, 0x000339f2, 0x00030a89, 0x0002db28,
+};
+
+static const u16 lpphy_a0_aux_gain_idx_table[] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014,
+};
+
+static const u32 lpphy_a0_gain_value_table[] = {
+	0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004,
+	0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a,
+	0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006,
+	0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000,
+	0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f,
+	0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7,
+	0x00000000, 0x00000000,
+};
+
+static const u16 lpphy_a0_gain_table[] = {
+	0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c,
+	0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a,
+	0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f,
+	0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157,
+	0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u16 lpphy_sw_control_table[] = {
+	0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128,
+	0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009,
+	0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018,
+	0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009,
+	0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028,
+	0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009,
+	0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018,
+	0x0018,
+};
+
+static const u8 lpphy_hf_table[] = {
+	0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48,
+	0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17,
+};
+
+static const u32 lpphy_papd_eps_table[] = {
+	0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf,
+	0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5,
+	0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b,
+	0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c,
+	0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16,
+	0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c,
+	0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4,
+	0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135,
+	0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf,
+	0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356,
+	0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506,
+};
+
+static const u32 lpphy_papd_mult_table[] = {
+	0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065,
+	0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f,
+	0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca,
+	0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e,
+	0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193,
+	0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a,
+	0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325,
+	0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471,
+	0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646,
+	0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd,
+	0x00036963, 0x000339f2, 0x00030a89, 0x0002db28,
+};
+
+struct lpphy_tx_gain_table_entry {
+	u8 gm,  pga,  pad,  dac,  bb_mult;
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev0_nopa_tx_gain_table[] = {
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev0_2ghz_tx_gain_table[] = {
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 57, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 83, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 81, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 78, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 76, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 74, },
+	{ .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 72, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev0_5ghz_tx_gain_table[] = {
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev1_nopa_tx_gain_table[] = {
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev1_2ghz_tx_gain_table[] = {
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 85, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 81, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 78, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 76, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 74, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, },
+	{ .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, },
+	{ .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, },
+	{ .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev1_5ghz_tx_gain_table[] = {
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, },
+	{ .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, },
+	{ .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev2_nopa_tx_gain_table[] = {
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 152, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 147, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 143, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 139, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 135, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 131, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 128, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 124, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 121, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 117, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 114, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 111, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 107, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 104, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 101, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 99, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 96, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 93, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 90, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 88, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 85, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 83, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 81, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 78, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 76, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 74, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 72, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 70, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 68, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 66, },
+	{ .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 192, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 176, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 171, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 157, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 144, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 140, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 136, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 105, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 91, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 86, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 79, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 248, .pad = 64, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 241, .pad = 62, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 234, .pad = 60, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 227, .pad = 59, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 221, .pad = 57, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 215, .pad = 55, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 208, .pad = 54, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 203, .pad = 52, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 197, .pad = 51, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 191, .pad = 49, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 186, .pad = 48, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 181, .pad = 47, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 175, .pad = 45, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 170, .pad = 44, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 166, .pad = 43, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 161, .pad = 42, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 156, .pad = 40, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 152, .pad = 39, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 148, .pad = 38, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 143, .pad = 37, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 139, .pad = 36, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 135, .pad = 35, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 132, .pad = 34, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 128, .pad = 33, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 124, .pad = 32, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 124, .pad = 31, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 121, .pad = 31, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 121, .pad = 30, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 117, .pad = 30, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 117, .pad = 29, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 111, .pad = 29, .dac = 0, .bb_mult = 64, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev2_2ghz_tx_gain_table[] = {
+	{ .gm = 7, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 31, .pad = 128, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 31, .pad = 124, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 30, .pad = 124, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 30, .pad = 121, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 29, .pad = 121, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 29, .pad = 114, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 28, .pad = 114, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 28, .pad = 111, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 27, .pad = 111, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 27, .pad = 108, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 26, .pad = 108, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 26, .pad = 104, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 25, .pad = 104, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 25, .pad = 99, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 24, .pad = 99, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 24, .pad = 96, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 23, .pad = 96, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 23, .pad = 90, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 22, .pad = 90, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 22, .pad = 88, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 21, .pad = 88, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 21, .pad = 83, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 20, .pad = 83, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 20, .pad = 78, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 19, .pad = 78, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 19, .pad = 74, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 18, .pad = 74, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 18, .pad = 70, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 17, .pad = 70, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 17, .pad = 66, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 16, .pad = 66, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 16, .pad = 62, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 62, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 15, .pad = 59, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 59, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 14, .pad = 54, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 54, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, },
+	{ .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, },
+};
+
+static struct lpphy_tx_gain_table_entry lpphy_rev2_5ghz_tx_gain_table[] = {
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 152, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 147, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 143, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 139, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 135, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 131, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 128, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 124, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 121, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 117, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 114, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 111, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 107, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 104, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 101, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 99, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 96, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 93, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 90, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 88, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 85, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 83, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 81, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 78, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 76, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 74, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 72, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 70, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 68, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 66, },
+	{ .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 248, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 241, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 234, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 227, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 221, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 215, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 208, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 203, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 197, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 191, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 186, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 181, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 175, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 170, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 166, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 161, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 156, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 152, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 148, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 143, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 139, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 135, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 132, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 128, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 124, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 121, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 117, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 114, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 111, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 108, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 104, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 102, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, },
+	{ .gm = 255, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, },
+};
+
+void lpphy_rev0_1_table_init(struct b43_wldev *dev)
+{
+	B43_WARN_ON(dev->phy.rev >= 2);
+
+	b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0),
+		ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0),
+		ARRAY_SIZE(lpphy_rev01_noise_scale_table), lpphy_rev01_noise_scale_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0),
+		ARRAY_SIZE(lpphy_crs_gain_nft_table), lpphy_crs_gain_nft_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(8, 0),
+		ARRAY_SIZE(lpphy_rev01_filter_control_table), lpphy_rev01_filter_control_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0),
+		ARRAY_SIZE(lpphy_rev01_ps_control_table), lpphy_rev01_ps_control_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0),
+		ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0),
+		ARRAY_SIZE(lpphy_iq_local_table), lpphy_iq_local_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0),
+		ARRAY_SIZE(lpphy_ofdm_cck_gain_table), lpphy_ofdm_cck_gain_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0),
+		ARRAY_SIZE(lpphy_ofdm_cck_gain_table), lpphy_ofdm_cck_gain_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0),
+		ARRAY_SIZE(lpphy_gain_delta_table), lpphy_gain_delta_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0),
+		ARRAY_SIZE(lpphy_tx_power_control_table), lpphy_tx_power_control_table);
+}
+
+void lpphy_rev2plus_table_init(struct b43_wldev *dev)
+{
+	struct ssb_bus *bus = dev->dev->bus;
+	int i;
+
+	B43_WARN_ON(dev->phy.rev < 2);
+
+	/*
+	 * FIXME This code follows the specs, but it looks wrong:
+	 * In each pass, it writes 4 bytes to an offset in table ID 7,
+	 * then increments the offset by 1 for the next pass. This results
+	 * in the first 3 bytes of each pass except the first one getting
+	 * written to a location that has already ben zeroed in the previous
+	 * pass.
+	 * This is what the vendor driver does, but it still looks suspicious.
+	 *
+	 * This should probably suffice:
+	 * 
+	 * for (i = 0; i < 704; i+=4)
+	 * 	b43_lptab_write(dev, B43_LPTAB32(7, i), 0)
+	 *
+	 * This should be tested once the code is functional.
+	 */
+	for (i = 0; i < 704; i++)
+		b43_lptab_write(dev, B43_LPTAB32(7, i), 0);
+
+	b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0),
+		ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0),
+		ARRAY_SIZE(lpphy_rev2plus_noise_scale_table), lpphy_rev2plus_noise_scale_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(11, 0),
+		ARRAY_SIZE(lpphy_rev2plus_filter_control_table), lpphy_rev2plus_filter_control_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(12, 0),
+		ARRAY_SIZE(lpphy_rev2plus_ps_control_table), lpphy_rev2plus_ps_control_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0),
+		ARRAY_SIZE(lpphy_gain_idx_table), lpphy_gain_idx_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0),
+		ARRAY_SIZE(lpphy_aux_gain_idx_table), lpphy_aux_gain_idx_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0),
+		ARRAY_SIZE(lpphy_sw_control_table), lpphy_sw_control_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB8(16, 0),
+		ARRAY_SIZE(lpphy_hf_table), lpphy_hf_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0),
+		ARRAY_SIZE(lpphy_gain_value_table), lpphy_gain_value_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0),
+		ARRAY_SIZE(lpphy_gain_table), lpphy_gain_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0),
+		ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0),
+		ARRAY_SIZE(lpphy_iq_local_table), lpphy_iq_local_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0),
+		ARRAY_SIZE(lpphy_papd_eps_table), lpphy_papd_eps_table);
+	b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0),
+		ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table);
+
+	if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+		b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0),
+			ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table);
+		b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0),
+			ARRAY_SIZE(lpphy_a0_aux_gain_idx_table), lpphy_a0_aux_gain_idx_table);
+		b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0),
+			ARRAY_SIZE(lpphy_a0_gain_value_table), lpphy_a0_gain_value_table);
+		b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0),
+			ARRAY_SIZE(lpphy_a0_gain_table), lpphy_a0_gain_table);
+	}
+}
+
+
+static void lpphy_rev0_1_write_gain_table(struct b43_wldev *dev,
+				struct lpphy_tx_gain_table_entry *table)
+{
+	int i;
+	u32 tmp;
+
+	B43_WARN_ON(dev->phy.rev >= 2);
+
+	for (i = 0; i < 128; i++) {
+		tmp  = table[i].pad << 11;
+		tmp |= table[i].pga << 7;
+		tmp |= table[i].gm  << 4;
+		tmp |= table[i].dac;
+		b43_lptab_write(dev, B43_LPTAB32(10, 0xC0 + i), tmp);
+		tmp  = table[i].bb_mult << 20;
+		b43_lptab_write(dev, B43_LPTAB32(10, 0x140 + i), tmp);
+	}
+}
+
+static void lpphy_rev2plus_write_gain_table(struct b43_wldev *dev,
+				struct lpphy_tx_gain_table_entry *table)
+{
+	int i;
+	u32 tmp;
+
+	B43_WARN_ON(dev->phy.rev < 2);
+
+	for (i = 0; i < 128; i++) {
+		tmp  = table[i].pad << 16;
+		tmp |= table[i].pga << 8;
+		tmp |= table[i].gm;
+		tmp |= 0x7f000000;
+		b43_lptab_write(dev, B43_LPTAB32(7, 0xC0 + i), tmp);
+		tmp  = table[i].bb_mult << 20;
+		tmp |= table[i].dac << 28;
+		b43_lptab_write(dev, B43_LPTAB32(7, 0x140 + i), tmp);
+	}
+}
+
+void lpphy_init_tx_gain_table(struct b43_wldev *dev)
+{
+	struct ssb_bus *bus = dev->dev->bus;
+
+	switch (dev->phy.rev) {
+	case 0:
+		if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) ||
+		    (bus->sprom.boardflags_lo & B43_BFL_HGPA))
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev0_nopa_tx_gain_table);
+		else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev0_2ghz_tx_gain_table);
+		else
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev0_5ghz_tx_gain_table);
+		break;
+	case 1:
+		if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) ||
+		    (bus->sprom.boardflags_lo & B43_BFL_HGPA))
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev1_nopa_tx_gain_table);
+		else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev1_2ghz_tx_gain_table);
+		else
+			lpphy_rev0_1_write_gain_table(dev,
+					lpphy_rev1_5ghz_tx_gain_table);
+		break;
+	default:
+		if (bus->sprom.boardflags_hi & B43_BFH_NOPA)
+			lpphy_rev2plus_write_gain_table(dev,
+					lpphy_rev2_nopa_tx_gain_table);
+		else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+			lpphy_rev2plus_write_gain_table(dev,
+					lpphy_rev2_2ghz_tx_gain_table);
+		else
+			lpphy_rev2plus_write_gain_table(dev,
+					lpphy_rev2_5ghz_tx_gain_table);
+	}
+}
diff --git a/drivers/net/wireless/b43/tables_lpphy.h b/drivers/net/wireless/b43/tables_lpphy.h
index 52ce32f..b5024b6 100644
--- a/drivers/net/wireless/b43/tables_lpphy.h
+++ b/drivers/net/wireless/b43/tables_lpphy.h
@@ -28,5 +28,8 @@ void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset,
 void b2062_upload_init_table(struct b43_wldev *dev);
 void b2063_upload_init_table(struct b43_wldev *dev);
 
+void lpphy_rev0_1_table_init(struct b43_wldev *dev);
+void lpphy_rev2plus_table_init(struct b43_wldev *dev);
+void lpphy_init_tx_gain_table(struct b43_wldev *dev);
 
 #endif /* B43_TABLES_LPPHY_H_ */
-- 
1.6.2.4



^ permalink raw reply related

* [PATCH] b43: Update LP-PHY rev2+ baseband init to match the specs
From: Gábor Stefanik @ 2009-08-10 18:42 UTC (permalink / raw)
  To: John Linville, Michael Buesch
  Cc: Larry Finger, Johannes Berg, Broadcom Wireless, linux-wireless

The rev2+ BB init spec has changed behind us, and thus the code is
no longer up to date. Update the code to match the current specs.
Also implement "save/restore dig filt state", as required by the
new specification (implemented as 2 separate functions).

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
 drivers/net/wireless/b43/phy_lp.c |   73 +++++++++++++++++++++++++++++++++++--
 drivers/net/wireless/b43/phy_lp.h |    3 ++
 2 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index d2af924..128d75a 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -195,6 +195,56 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
 	}
 }
 
+static void lpphy_save_dig_flt_state(struct b43_wldev *dev)
+{
+	static const u16 addr[] = {
+		B43_PHY_OFDM(0xC1),
+		B43_PHY_OFDM(0xC2),
+		B43_PHY_OFDM(0xC3),
+		B43_PHY_OFDM(0xC4),
+		B43_PHY_OFDM(0xC5),
+		B43_PHY_OFDM(0xC6),
+		B43_PHY_OFDM(0xC7),
+		B43_PHY_OFDM(0xC8),
+		B43_PHY_OFDM(0xCF),
+	};
+
+	static const u16 coeffs[] = {
+		0xDE5E, 0xE832, 0xE331, 0x4D26,
+		0x0026, 0x1420, 0x0020, 0xFE08,
+		0x0008,
+	};
+
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	int i;
+
+	for (i = 0; i < 9; i++) {
+		lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]);
+		b43_phy_write(dev, addr[i], coefs[i]);
+	}
+}
+
+static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
+{
+	static const u16 addr[] = {
+		B43_PHY_OFDM(0xC1),
+		B43_PHY_OFDM(0xC2),
+		B43_PHY_OFDM(0xC3),
+		B43_PHY_OFDM(0xC4),
+		B43_PHY_OFDM(0xC5),
+		B43_PHY_OFDM(0xC6),
+		B43_PHY_OFDM(0xC7),
+		B43_PHY_OFDM(0xC8),
+		B43_PHY_OFDM(0xCF),
+	};
+
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	int i;
+
+	for (i = 0; i < 9; i++)
+		b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]);
+}
+
 static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 {
 	struct ssb_bus *bus = dev->dev->bus;
@@ -209,7 +259,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0);
 	b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0);
 	b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10);
-	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x78);
+	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4);
 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200);
 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F);
 	b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40);
@@ -217,7 +267,12 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000);
 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000);
 	b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1);
-	b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
+	if (bus->boardinfo.rev >= 0x18) {
+		b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC);
+		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14);
+	} else {
+		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
+	}
 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4);
 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100);
 	b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48);
@@ -247,8 +302,10 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12);
 	b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000);
 
-	b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
-	b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
+	if ((bus->chip_id == 0x4325) && (bus->chip_rev == 1)) {
+		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
+		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
+	}
 
 	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40);
@@ -268,6 +325,14 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1,
 		      0x2000 | ((u16)lpphy->rssi_gs << 10) |
 		      ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf);
+
+	if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+		b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C);
+		b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800);
+		b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400);
+	}
+
+	lpphy_save_dig_flt_state(dev);
 }
 
 static void lpphy_baseband_init(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h
index 829b2bb..13d89ea 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/b43/phy_lp.h
@@ -865,6 +865,9 @@ struct b43_phy_lp {
 	/* Transmit iqlocal best coeffs */
 	bool tx_iqloc_best_coeffs_valid;
 	u8 tx_iqloc_best_coeffs[11];
+
+	/* Used for "Save/Restore Dig Filt State" */
+	u16 dig_flt_state[9];
 };
 
 
-- 
1.6.2.4



^ permalink raw reply related

* Re: [PATCH] b43: Implement LP-PHY baseband table initialization
From: Michael Buesch @ 2009-08-10 18:43 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Larry Finger, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <4A806973.1020109@gmail.com>

On Monday 10 August 2009 20:39:47 Gábor Stefanik wrote:
> Implement LP-PHY baseband table init for all revisions.
> 
> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
> ---
> Changes from RFC:
> -Improved table formatting in the code.
> -The 2GHz check in adjust_gain_table now uses b43_current_band.
> -Added a comment to rev2plus_table_init about a possible future improvement.

ack
-- 
Greetings, Michael.

^ permalink raw reply

* Re: [PATCH] b43: Update LP-PHY rev2+ baseband init to match the specs
From: Michael Buesch @ 2009-08-10 18:46 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Larry Finger, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <4A806A19.2070209@gmail.com>

On Monday 10 August 2009 20:42:33 Gábor Stefanik wrote:
> +static void lpphy_save_dig_flt_state(struct b43_wldev *dev)
> +{
> +	static const u16 addr[] = {
> +		B43_PHY_OFDM(0xC1),
> +		B43_PHY_OFDM(0xC2),
> +		B43_PHY_OFDM(0xC3),
> +		B43_PHY_OFDM(0xC4),
> +		B43_PHY_OFDM(0xC5),
> +		B43_PHY_OFDM(0xC6),
> +		B43_PHY_OFDM(0xC7),
> +		B43_PHY_OFDM(0xC8),
> +		B43_PHY_OFDM(0xCF),
> +	};
> +
> +	static const u16 coeffs[] = {
> +		0xDE5E, 0xE832, 0xE331, 0x4D26,
> +		0x0026, 0x1420, 0x0020, 0xFE08,
> +		0x0008,
> +	};
> +
> +	struct b43_phy_lp *lpphy = dev->phy.lp;
> +	int i;
> +
> +	for (i = 0; i < 9; i++) {

Use ARRAY_SIZE, please.

> +		lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]);
> +		b43_phy_write(dev, addr[i], coefs[i]);
> +	}
> +}
> +
> +static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
> +{
> +	static const u16 addr[] = {
> +		B43_PHY_OFDM(0xC1),
> +		B43_PHY_OFDM(0xC2),
> +		B43_PHY_OFDM(0xC3),
> +		B43_PHY_OFDM(0xC4),
> +		B43_PHY_OFDM(0xC5),
> +		B43_PHY_OFDM(0xC6),
> +		B43_PHY_OFDM(0xC7),
> +		B43_PHY_OFDM(0xC8),
> +		B43_PHY_OFDM(0xCF),
> +	};
> +
> +	struct b43_phy_lp *lpphy = dev->phy.lp;
> +	int i;
> +
> +	for (i = 0; i < 9; i++)

Same here.

> +		b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]);
> +}
> +


-- 
Greetings, Michael.

^ permalink raw reply

* [PATCH RFC] ssb: Implement the remaining rev.8 SPROM vars needed for LP-PHY
From: Gábor Stefanik @ 2009-08-10 18:49 UTC (permalink / raw)
  To: John Linville, Michael Buesch
  Cc: Larry Finger, Johannes Berg, Broadcom Wireless, linux-wireless

Also add a "SPEX32" macro for extracting 32-bit SPROM variables.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
I'm not quite sure that the SPEX32 macro is sane endianness-wise;
please review it carefully. (In the future, we will probably need
a SPEX64 macro too, to correctly extract boardflags.)

 drivers/ssb/pci.c            |   53 +++++++++++++++++++++++++++++++++-
 include/linux/ssb/ssb.h      |   44 +++++++++++++++++++++++----
 include/linux/ssb/ssb_regs.h |   66 +++++++++++++++++++++++++++++++++++++----
 3 files changed, 148 insertions(+), 15 deletions(-)

diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 40ea417..50811e4 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -169,8 +169,14 @@ err_pci:
 /* Get the word-offset for a SSB_SPROM_XXX define. */
 #define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
 /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
-#define SPEX(_outvar, _offset, _mask, _shift)	\
+#define SPEX16(_outvar, _offset, _mask, _shift)	\
 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
+#define SPEX32(_outvar, _offset, _mask, _shift)	\
+	out->_outvar = ((((in[SPOFF((_offset)+2)] << sizeof(u16)) | \
+			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
+#define SPEX(_outvar, _offset, _mask, _shift) \
+	SPEX16(_outvar, _offset, _mask, _shift)
+
 
 static inline u8 ssb_crc8(u8 crc, u8 data)
 {
@@ -480,6 +486,8 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 	SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0);
 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
+	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
+	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
@@ -490,12 +498,55 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
 	     SSB_SPROM8_ITSSI_A_SHIFT);
+	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
+	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
+	     SSB_SPROM8_MAXP_AL_SHIFT);
 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
 	     SSB_SPROM8_GPIOA_P1_SHIFT);
 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
 	     SSB_SPROM8_GPIOB_P3_SHIFT);
+	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
+	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
+	     SSB_SPROM8_TRI5G_SHIFT);
+	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
+	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
+	     SSB_SPROM8_TRI5GH_SHIFT);
+	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
+	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
+	     SSB_SPROM8_RXPO5G_SHIFT);
+	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
+	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
+	     SSB_SPROM8_RSSISMC2G_SHIFT);
+	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
+	     SSB_SPROM8_RSSISAV2G_SHIFT);
+	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
+	     SSB_SPROM8_BXA2G_SHIFT);
+	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
+	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
+	     SSB_SPROM8_RSSISMC5G_SHIFT);
+	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
+	     SSB_SPROM8_RSSISAV5G_SHIFT);
+	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
+	     SSB_SPROM8_BXA5G_SHIFT);
+	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
+	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
+	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
+	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
+	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
+	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
+	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
+	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
+	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
+	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
+	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
+	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
+	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
+	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
 
 	/* Extract the antenna gain values. */
 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01,
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index 5ae8fa2..17ffc1f 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -27,24 +27,54 @@ struct ssb_sprom {
 	u8 et1mdcport;		/* MDIO for enet1 */
 	u8 board_rev;		/* Board revision number from SPROM. */
 	u8 country_code;	/* Country Code */
-	u8 ant_available_a;	/* A-PHY antenna available bits (up to 4) */
-	u8 ant_available_bg;	/* B/G-PHY antenna available bits (up to 4) */
+	u8 ant_available_a;	/* 2GHz antenna available bits (up to 4) */
+	u8 ant_available_bg;	/* 5GHz antenna available bits (up to 4) */
 	u16 pa0b0;
 	u16 pa0b1;
 	u16 pa0b2;
 	u16 pa1b0;
 	u16 pa1b1;
 	u16 pa1b2;
+	u16 pa1lob0;
+	u16 pa1lob1;
+	u16 pa1lob2;
+	u16 pa1hib0;
+	u16 pa1hib1;
+	u16 pa1hib2;
 	u8 gpio0;		/* GPIO pin 0 */
 	u8 gpio1;		/* GPIO pin 1 */
 	u8 gpio2;		/* GPIO pin 2 */
 	u8 gpio3;		/* GPIO pin 3 */
-	u16 maxpwr_a;		/* A-PHY Amplifier Max Power (in dBm Q5.2) */
-	u16 maxpwr_bg;		/* B/G-PHY Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_bg;		/* 2.4GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_al;		/* 5.2GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_a;		/* 5.3GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_ah;		/* 5.8GHz Amplifier Max Power (in dBm Q5.2) */
 	u8 itssi_a;		/* Idle TSSI Target for A-PHY */
 	u8 itssi_bg;		/* Idle TSSI Target for B/G-PHY */
-	u16 boardflags_lo;	/* Boardflags (low 16 bits) */
-	u16 boardflags_hi;	/* Boardflags (high 16 bits) */
+	u8 tri2g;		/* 2.4GHz TX isolation */
+	u8 tri5gl;		/* 5.2GHz TX isolation */
+	u8 tri5g;		/* 5.3GHz TX isolation */
+	u8 tri5gh;		/* 5.8GHz TX isolation */
+	u8 rxpo2g;		/* 2GHz RX power offset */
+	u8 rxpo5g;		/* 5GHz RX power offset */
+	u8 rssisav2g;		/* 2GHz RSSI params */
+	u8 rssismc2g;
+	u8 rssismf2g;
+	u8 bxa2g;		/* 2GHz BX arch */
+	u8 rssisav5g;		/* 5GHz RSSI params */
+	u8 rssismc5g;
+	u8 rssismf5g;
+	u8 bxa5g;		/* 5GHz BX arch */
+	u16 cck2gpo;		/* CCK power offset */
+	u32 ofdm2gpo;		/* 2.4GHz OFDM power offset */
+	u32 ofdm5glpo;		/* 5.2GHz OFDM power offset */
+	u32 ofdm5gpo;		/* 5.3GHz OFDM power offset */
+	u32 ofdm5ghpo;		/* 5.8GHz OFDM power offset */
+	u16 boardflags_lo;	/* Board flags (bits 0-15) */
+	u16 boardflags_hi;	/* Board flags (bits 16-31) */
+	u16 boardflags2_lo;	/* Board flags (bits 32-47) */
+	u16 boardflags2_hi;	/* Board flags (bits 48-63) */
+	/* TODO store board flags in a single u64 */
 
 	/* Antenna gain values for up to 4 antennas
 	 * on each band. Values in dBm/4 (Q5.2). Negative gain means the
@@ -58,7 +88,7 @@ struct ssb_sprom {
 		} ghz5;		/* 5GHz band */
 	} antenna_gain;
 
-	/* TODO - add any parameters needed from rev 2, 3, or 4 SPROMs */
+	/* TODO - add any parameters needed from rev 2, 3, 4, 5 or 8 SPROMs */
 };
 
 /* Information about the PCB the circuitry is soldered on. */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index a01b982..9ae9082 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -162,7 +162,7 @@
 
 /* SPROM shadow area. If not otherwise noted, fields are
  * two bytes wide. Note that the SPROM can _only_ be read
- * in two-byte quantinies.
+ * in two-byte quantities.
  */
 #define SSB_SPROMSIZE_WORDS		64
 #define SSB_SPROMSIZE_BYTES		(SSB_SPROMSIZE_WORDS * sizeof(u16))
@@ -327,8 +327,11 @@
 #define  SSB_SPROM5_GPIOB_P3_SHIFT	8
 
 /* SPROM Revision 8 */
-#define SSB_SPROM8_BFLLO		0x1084	/* Boardflags (low 16 bits) */
-#define SSB_SPROM8_BFLHI		0x1086	/* Boardflags Hi */
+#define SSB_SPROM8_BOARDREV		0x1082	/* Board revision */
+#define SSB_SPROM8_BFLLO		0x1084	/* Board flags (bits 0-15) */
+#define SSB_SPROM8_BFLHI		0x1086	/* Board flags (bits 16-31) */
+#define SSB_SPROM8_BFL2LO		0x1088	/* Board flags (bits 32-47) */
+#define SSB_SPROM8_BFL2HI		0x108A	/* Board flags (bits 48-63) */
 #define SSB_SPROM8_IL0MAC		0x108C	/* 6 byte MAC address */
 #define SSB_SPROM8_CCODE		0x1092	/* 2 byte country code */
 #define SSB_SPROM8_ANTAVAIL		0x109C  /* Antenna available bitfields*/
@@ -354,14 +357,63 @@
 #define  SSB_SPROM8_GPIOB_P2		0x00FF	/* Pin 2 */
 #define  SSB_SPROM8_GPIOB_P3		0xFF00	/* Pin 3 */
 #define  SSB_SPROM8_GPIOB_P3_SHIFT	8
-#define SSB_SPROM8_MAXP_BG		0x10C0  /* Max Power BG in path 1 */
-#define  SSB_SPROM8_MAXP_BG_MASK	0x00FF  /* Mask for Max Power BG */
+#define SSB_SPROM8_RSSIPARM2G		0x10A4	/* RSSI params for 2GHz */
+#define  SSB_SPROM8_RSSISMF2G		0x000F
+#define  SSB_SPROM8_RSSISMC2G		0x00F0
+#define  SSB_SPROM8_RSSISMC2G_SHIFT	4
+#define  SSB_SPROM8_RSSISAV2G		0x0700
+#define  SSB_SPROM8_RSSISAV2G_SHIFT	8
+#define  SSB_SPROM8_BXA2G		0x1800
+#define  SSB_SPROM8_BXA2G_SHIFT		11
+#define SSB_SPROM8_RSSIPARM5G		0x10A6	/* RSSI params for 5GHz */
+#define  SSB_SPROM8_RSSISMF5G		0x000F
+#define  SSB_SPROM8_RSSISMC5G		0x00F0
+#define  SSB_SPROM8_RSSISMC5G_SHIFT	4
+#define  SSB_SPROM8_RSSISAV5G		0x0700
+#define  SSB_SPROM8_RSSISAV5G_SHIFT	8
+#define  SSB_SPROM8_BXA5G		0x1800
+#define  SSB_SPROM8_BXA5G_SHIFT		11
+#define SSB_SPROM8_TRI25G		0x10A8	/* TX isolation 2.4&5.3GHz */
+#define  SSB_SPROM8_TRI2G		0x00FF	/* TX isolation 2.4GHz */
+#define  SSB_SPROM8_TRI5G		0xFF00	/* TX isolation 5.3GHz */
+#define  SSB_SPROM8_TRI5G_SHIFT		8
+#define SSB_SPROM8_TRI5GHL		0x10AA	/* TX isolation 5.2/5.8GHz */
+#define  SSB_SPROM8_TRI5GL		0x00FF	/* TX isolation 5.2GHz */
+#define  SSB_SPROM8_TRI5GH		0xFF00	/* TX isolation 5.8GHz */
+#define  SSB_SPROM8_TRI5GH_SHIFT	8
+#define SSB_SPROM8_RXPO			0x10AC  /* RX power offsets */
+#define  SSB_SPROM8_RXPO2G		0x00FF	/* 2GHz RX power offset */
+#define  SSB_SPROM8_RXPO5G		0xFF00	/* 5GHz RX power offset */
+#define  SSB_SPROM8_RXPO5G_SHIFT	8
+#define SSB_SPROM8_MAXP_BG		0x10C0  /* Max Power 2GHz in path 1 */
+#define  SSB_SPROM8_MAXP_BG_MASK	0x00FF  /* Mask for Max Power 2GHz */
 #define  SSB_SPROM8_ITSSI_BG		0xFF00	/* Mask for path 1 itssi_bg */
 #define  SSB_SPROM8_ITSSI_BG_SHIFT	8
-#define SSB_SPROM8_MAXP_A		0x10C8  /* Max Power A in path 1 */
-#define  SSB_SPROM8_MAXP_A_MASK		0x00FF  /* Mask for Max Power A */
+#define SSB_SPROM8_PA0B0		0x10C2	/* 2GHz power amp settings */
+#define SSB_SPROM8_PA0B1		0x10C4
+#define SSB_SPROM8_PA0B2		0x10C6
+#define SSB_SPROM8_MAXP_A		0x10C8  /* Max Power 5.3GHz */
+#define  SSB_SPROM8_MAXP_A_MASK		0x00FF  /* Mask for Max Power 5.3GHz */
 #define  SSB_SPROM8_ITSSI_A		0xFF00	/* Mask for path 1 itssi_a */
 #define  SSB_SPROM8_ITSSI_A_SHIFT	8
+#define SSB_SPROM8_MAXP_AHL		0x10CA  /* Max Power 5.2/5.8GHz */
+#define  SSB_SPROM8_MAXP_AH_MASK	0x00FF  /* Mask for Max Power 5.8GHz */
+#define  SSB_SPROM8_MAXP_AL_MASK	0xFF00  /* Mask for Max Power 5.2GHz */
+#define  SSB_SPROM8_MAXP_AL_SHIFT	8
+#define SSB_SPROM8_PA1B0		0x10CC	/* 5.3GHz power amp settings */
+#define SSB_SPROM8_PA1B1		0x10CE
+#define SSB_SPROM8_PA1B2		0x10D0
+#define SSB_SPROM8_PA1LOB0		0x10D2	/* 5.2GHz power amp settings */
+#define SSB_SPROM8_PA1LOB1		0x10D4
+#define SSB_SPROM8_PA1LOB2		0x10D6
+#define SSB_SPROM8_PA1HIB0		0x10D8	/* 5.8GHz power amp settings */
+#define SSB_SPROM8_PA1HIB1		0x10DA
+#define SSB_SPROM8_PA1HIB2		0x10DC
+#define SSB_SPROM8_CCK2GPO		0x1140	/* CCK power offset */
+#define SSB_SPROM8_OFDM2GPO		0x1142	/* 2.4GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GPO		0x1146	/* 5.3GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GLPO		0x114A	/* 5.2GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GHPO		0x114E	/* 5.8GHz OFDM power offset */
 
 /* Values for SSB_SPROM1_BINF_CCODE */
 enum {
-- 
1.6.2.4



^ permalink raw reply related

* Re: [PATCH] b43: Implement LP-PHY baseband table initialization
From: Larry Finger @ 2009-08-10 18:51 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Michael Buesch, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <4A806973.1020109@gmail.com>

Gábor Stefanik wrote:
> Implement LP-PHY baseband table init for all revisions.
> 
> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
> ---
> Changes from RFC:
> -Improved table formatting in the code.
> -The 2GHz check in adjust_gain_table now uses b43_current_band.
> -Added a comment to rev2plus_table_init about a possible future
> improvement.

I like the new table formatting.

The comment in rev2plus_table_init has a typo - "ben" should be
"been". Otherwise it looks good. Thanks for all your work.

Larry

^ permalink raw reply

* Re: [PATCH 2.6.31] ar9170: fix read & write outside array bounds
From: Chunkeey @ 2009-08-10 18:56 UTC (permalink / raw)
  To: John W. Linville; +Cc: Dan Carpenter, wireless

"John W. Linville" <linville@tuxdriver.com> wrote:
> On Sun, Aug 09, 2009 at 02:24:09PM +0200, Christian Lamparter wrote:
> > From: Dan Carpenter <error27@gmail.com>
> > 
> > queue == __AR9170_NUM_TXQ would cause a bug on the next line.
> > 
> > found by Smatch ( http://repo.or.cz/w/smatch.git ).
> > 
> > Cc: stable@kernel.org
> > Reported-by: Dan Carpenter <error27@gmail.com>
> > Signed-off-by: Dan Carpenter <error27@gmail.com>
> > Signed-off-by: Christian Lamparter <chunkeey@web.de>
> > ---
> > diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
> > index 4fc389a..ea8c941 100644
> > --- a/drivers/net/wireless/ath/ar9170/main.c
> > +++ b/drivers/net/wireless/ath/ar9170/main.c
> > @@ -2458,13 +2458,14 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
> >  	int ret;
> >  
> >  	mutex_lock(&ar->mutex);
> > -	if ((param) && !(queue > __AR9170_NUM_TXQ)) {
> > +	if (queue < __AR9170_NUM_TXQ) {
> >  		memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
> >  		       param, sizeof(*param));
> >  
> >  		ret = ar9170_set_qos(ar);
> > -	} else
> > +	} else {
> >  		ret = -EINVAL;
> > +	}
> >  
> >  	mutex_unlock(&ar->mutex);
> >  	return ret;
> 
> The p54 version of this patch used hw->queues instead of a constant.
> Wouldn't that be better here?
Depends...

Other drivers like ath9k/iwlwifi use a constant _check_ as well.
Having constants is always a plus for AOT compilers.

The reason why p54 does this differently and uses a variable
here, is simply because  the number of queues depends on
the firmware revision. Users - with the old, original windows
driver firmwares - have experienced serve stability problems
with QoS enabled.

Regards,
       Chr

________________________________________________________________
Neu: WEB.DE Doppel-FLAT mit Internet-Flatrate + Telefon-Flatrate
für nur 19,99 Euro/mtl.!* http://produkte.web.de/go/02/


^ permalink raw reply

* [PATCH V2] b43: Update LP-PHY rev2+ baseband init to match the specs
From: Gábor Stefanik @ 2009-08-10 18:57 UTC (permalink / raw)
  To: John Linville, Michael Buesch
  Cc: Larry Finger, Johannes Berg, Broadcom Wireless, linux-wireless

The rev2+ BB init spec has changed behind us, and thus the code is
no longer up to date. Update the code to match the current specs.
Also implement save/restore dig filt state, as required by the
new specification.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
V2: use ARRAY_SIZE for loop counts.

 drivers/net/wireless/b43/phy_lp.c |   73 +++++++++++++++++++++++++++++++++++--
 drivers/net/wireless/b43/phy_lp.h |    3 ++
 2 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index d2af924..128d75a 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -195,6 +195,56 @@ static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
 	}
 }
 
+static void lpphy_save_dig_flt_state(struct b43_wldev *dev)
+{
+	static const u16 addr[] = {
+		B43_PHY_OFDM(0xC1),
+		B43_PHY_OFDM(0xC2),
+		B43_PHY_OFDM(0xC3),
+		B43_PHY_OFDM(0xC4),
+		B43_PHY_OFDM(0xC5),
+		B43_PHY_OFDM(0xC6),
+		B43_PHY_OFDM(0xC7),
+		B43_PHY_OFDM(0xC8),
+		B43_PHY_OFDM(0xCF),
+	};
+
+	static const u16 coefs[] = {
+		0xDE5E, 0xE832, 0xE331, 0x4D26,
+		0x0026, 0x1420, 0x0020, 0xFE08,
+		0x0008,
+	};
+
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(addr); i++) {
+		lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]);
+		b43_phy_write(dev, addr[i], coefs[i]);
+	}
+}
+
+static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
+{
+	static const u16 addr[] = {
+		B43_PHY_OFDM(0xC1),
+		B43_PHY_OFDM(0xC2),
+		B43_PHY_OFDM(0xC3),
+		B43_PHY_OFDM(0xC4),
+		B43_PHY_OFDM(0xC5),
+		B43_PHY_OFDM(0xC6),
+		B43_PHY_OFDM(0xC7),
+		B43_PHY_OFDM(0xC8),
+		B43_PHY_OFDM(0xCF),
+	};
+
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(addr); i++)
+		b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]);
+}
+
 static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 {
 	struct ssb_bus *bus = dev->dev->bus;
@@ -209,7 +259,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0);
 	b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0);
 	b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10);
-	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x78);
+	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4);
 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200);
 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F);
 	b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40);
@@ -217,7 +267,12 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000);
 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000);
 	b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1);
-	b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
+	if (bus->boardinfo.rev >= 0x18) {
+		b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC);
+		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14);
+	} else {
+		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
+	}
 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4);
 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100);
 	b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48);
@@ -247,8 +302,10 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12);
 	b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000);
 
-	b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
-	b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
+	if ((bus->chip_id == 0x4325) && (bus->chip_rev == 1)) {
+		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
+		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
+	}
 
 	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40);
@@ -268,6 +325,14 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
 	b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1,
 		      0x2000 | ((u16)lpphy->rssi_gs << 10) |
 		      ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf);
+
+	if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) {
+		b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C);
+		b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800);
+		b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400);
+	}
+
+	lpphy_save_dig_flt_state(dev);
 }
 
 static void lpphy_baseband_init(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h
index 829b2bb..13d89ea 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/b43/phy_lp.h
@@ -865,6 +865,9 @@ struct b43_phy_lp {
 	/* Transmit iqlocal best coeffs */
 	bool tx_iqloc_best_coeffs_valid;
 	u8 tx_iqloc_best_coeffs[11];
+
+	/* Used for "Save/Restore Dig Filt State" */
+	u16 dig_flt_state[9];
 };
 
 
-- 
1.6.2.4



^ permalink raw reply related

* Re: [PATCH] b43: Implement LP-PHY baseband table initialization
From: Gábor Stefanik @ 2009-08-10 18:58 UTC (permalink / raw)
  To: John Linville, Larry Finger
  Cc: Michael Buesch, Johannes Berg, Broadcom Wireless, linux-wireless
In-Reply-To: <4A806C2F.9070206@lwfinger.net>

2009/8/10 Larry Finger <Larry.Finger@lwfinger.net>:
> Gábor Stefanik wrote:
>> Implement LP-PHY baseband table init for all revisions.
>>
>> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
>> ---
>> Changes from RFC:
>> -Improved table formatting in the code.
>> -The 2GHz check in adjust_gain_table now uses b43_current_band.
>> -Added a comment to rev2plus_table_init about a possible future
>> improvement.
>
> I like the new table formatting.
>
> The comment in rev2plus_table_init has a typo - "ben" should be
> "been". Otherwise it looks good. Thanks for all your work.
>
> Larry
>

John, can you fix that when checking in, or should I resubmit with the
typo fixed?

-- 
Vista: [V]iruses, [I]ntruders, [S]pyware, [T]rojans and [A]dware. :-)

^ permalink raw reply

* Re: [PATCH RFC] ssb: Implement the remaining rev.8 SPROM vars needed for LP-PHY
From: Michael Buesch @ 2009-08-10 19:01 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Larry Finger, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <4A806B9E.5010105@gmail.com>

On Monday 10 August 2009 20:49:02 Gábor Stefanik wrote:
> Also add a "SPEX32" macro for extracting 32-bit SPROM variables.
> 
> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
> ---
> I'm not quite sure that the SPEX32 macro is sane endianness-wise;
> please review it carefully. (In the future, we will probably need
> a SPEX64 macro too, to correctly extract boardflags.)
> 
>  drivers/ssb/pci.c            |   53 +++++++++++++++++++++++++++++++++-
>  include/linux/ssb/ssb.h      |   44 +++++++++++++++++++++++----
>  include/linux/ssb/ssb_regs.h |   66 +++++++++++++++++++++++++++++++++++++----
>  3 files changed, 148 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
> index 40ea417..50811e4 100644
> --- a/drivers/ssb/pci.c
> +++ b/drivers/ssb/pci.c
> @@ -169,8 +169,14 @@ err_pci:
>  /* Get the word-offset for a SSB_SPROM_XXX define. */
>  #define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
>  /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
> -#define SPEX(_outvar, _offset, _mask, _shift)	\
> +#define SPEX16(_outvar, _offset, _mask, _shift)	\
>  	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
> +#define SPEX32(_outvar, _offset, _mask, _shift)	\
> +	out->_outvar = ((((in[SPOFF((_offset)+2)] << sizeof(u16)) | \
                                                  ^^^^^^^^^^^^^^
This shifts by 2 bit. Did you want 16 bits? Just write  << 16  then.
Also, it seems you need +1 instead of +2 (it is an u16 pointer).
I also usually cast u16 values to u32 before shifting >=16 bits.
The result of shifting a 16bit variable left by 16bits is machine dependent.
It works as expected for every linux architecture, but I prefer the cast anyway.

Your macro extracts the value in littleendian format. I didn't check if this is
correct for your values. But there also are values stored in BE format.


-- 
Greetings, Michael.

^ permalink raw reply

* Re: [PATCH V2] b43: Update LP-PHY rev2+ baseband init to match the specs
From: Michael Buesch @ 2009-08-10 19:02 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: John Linville, Larry Finger, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <4A806D82.4010606@gmail.com>

On Monday 10 August 2009 20:57:06 Gábor Stefanik wrote:
> The rev2+ BB init spec has changed behind us, and thus the code is
> no longer up to date. Update the code to match the current specs.
> Also implement save/restore dig filt state, as required by the
> new specification.
> 
> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
> ---
> V2: use ARRAY_SIZE for loop counts.

ack

-- 
Greetings, Michael.

^ permalink raw reply

* Re: [PATCH RFC] ssb: Implement the remaining rev.8 SPROM vars needed for LP-PHY
From: Gábor Stefanik @ 2009-08-10 19:07 UTC (permalink / raw)
  To: Michael Buesch, Larry Finger
  Cc: John Linville, Johannes Berg, Broadcom Wireless, linux-wireless
In-Reply-To: <200908102101.38698.mb@bu3sch.de>

2009/8/10 Michael Buesch <mb@bu3sch.de>:
> On Monday 10 August 2009 20:49:02 Gábor Stefanik wrote:
>> Also add a "SPEX32" macro for extracting 32-bit SPROM variables.
>>
>> Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
>> ---
>> I'm not quite sure that the SPEX32 macro is sane endianness-wise;
>> please review it carefully. (In the future, we will probably need
>> a SPEX64 macro too, to correctly extract boardflags.)
>>
>>  drivers/ssb/pci.c            |   53 +++++++++++++++++++++++++++++++++-
>>  include/linux/ssb/ssb.h      |   44 +++++++++++++++++++++++----
>>  include/linux/ssb/ssb_regs.h |   66 +++++++++++++++++++++++++++++++++++++----
>>  3 files changed, 148 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
>> index 40ea417..50811e4 100644
>> --- a/drivers/ssb/pci.c
>> +++ b/drivers/ssb/pci.c
>> @@ -169,8 +169,14 @@ err_pci:
>>  /* Get the word-offset for a SSB_SPROM_XXX define. */
>>  #define SPOFF(offset)        (((offset) - SSB_SPROM_BASE) / sizeof(u16))
>>  /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
>> -#define SPEX(_outvar, _offset, _mask, _shift)        \
>> +#define SPEX16(_outvar, _offset, _mask, _shift)      \
>>       out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
>> +#define SPEX32(_outvar, _offset, _mask, _shift)      \
>> +     out->_outvar = ((((in[SPOFF((_offset)+2)] << sizeof(u16)) | \
>                                                  ^^^^^^^^^^^^^^
> This shifts by 2 bit. Did you want 16 bits? Just write  << 16  then.
Good catch, thanks!

> Also, it seems you need +1 instead of +2 (it is an u16 pointer).
I'm adding to _offset, not to SPOFF(_offset). AFAICS _offset is not an
u16 pointer, it's a plain #defined number, unlike SPOFF(_offset).

> I also usually cast u16 values to u32 before shifting >=16 bits.
> The result of shifting a 16bit variable left by 16bits is machine dependent.
> It works as expected for every linux architecture, but I prefer the cast anyway.
Will do, thanks.

>
> Your macro extracts the value in littleendian format. I didn't check if this is
> correct for your values. But there also are values stored in BE format.
Larry, do you have any comments on this?

>
>
> --
> Greetings, Michael.
>



-- 
Vista: [V]iruses, [I]ntruders, [S]pyware, [T]rojans and [A]dware. :-)

^ permalink raw reply

* Re: [PATCH RFC] ssb: Implement the remaining rev.8 SPROM vars needed  for LP-PHY
From: Michael Buesch @ 2009-08-10 19:12 UTC (permalink / raw)
  To: Gábor Stefanik
  Cc: Larry Finger, John Linville, Johannes Berg, Broadcom Wireless,
	linux-wireless
In-Reply-To: <69e28c910908101207p17139562v55ede047c944dae@mail.gmail.com>

On Monday 10 August 2009 21:07:45 Gábor Stefanik wrote:
> > Also, it seems you need +1 instead of +2 (it is an u16 pointer).
> I'm adding to _offset, not to SPOFF(_offset). AFAICS _offset is not an
> u16 pointer, it's a plain #defined number, unlike SPOFF(_offset).

Ah ok. All those parenthesis are a little bit confusing. 8-X

-- 
Greetings, Michael.

^ permalink raw reply

* [PATCH] ssb: Implement the remaining rev.8 SPROM vars needed for LP-PHY
From: Gábor Stefanik @ 2009-08-10 19:23 UTC (permalink / raw)
  To: John Linville, Michael Buesch, Larry Finger
  Cc: Johannes Berg, Broadcom Wireless, linux-wireless

Also add a "SPEX32" macro for extracting 32-bit SPROM variables.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
Changes from RFC:
-Fixed a bug in the SPEX32 macro.
-in[SPOFF...] is now cast to u32 to be extra-safe.

The macro still extracts little-endian, as that is probably
what the driver expects (at least for the regular SPEX macro) -
however, Larry said that SPEX is broken (it should extract big
endian), and the driver may be relying on incorrect behavior.
This should be looked into eventually.

 drivers/ssb/pci.c            |   53 +++++++++++++++++++++++++++++++++-
 include/linux/ssb/ssb.h      |   44 +++++++++++++++++++++++----
 include/linux/ssb/ssb_regs.h |   66 +++++++++++++++++++++++++++++++++++++----
 3 files changed, 148 insertions(+), 15 deletions(-)

diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 40ea417..50811e4 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -169,8 +169,14 @@ err_pci:
 /* Get the word-offset for a SSB_SPROM_XXX define. */
 #define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
 /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
-#define SPEX(_outvar, _offset, _mask, _shift)	\
+#define SPEX16(_outvar, _offset, _mask, _shift)	\
 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
+#define SPEX32(_outvar, _offset, _mask, _shift)	\
+	out->_outvar = (((((u32)in[SPOFF((_offset)+2)] << 16 | \
+			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
+#define SPEX(_outvar, _offset, _mask, _shift) \
+	SPEX16(_outvar, _offset, _mask, _shift)
+
 
 static inline u8 ssb_crc8(u8 crc, u8 data)
 {
@@ -480,6 +486,8 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 	SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0);
 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
+	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
+	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
@@ -490,12 +498,55 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
 	     SSB_SPROM8_ITSSI_A_SHIFT);
+	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
+	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
+	     SSB_SPROM8_MAXP_AL_SHIFT);
 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
 	     SSB_SPROM8_GPIOA_P1_SHIFT);
 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
 	     SSB_SPROM8_GPIOB_P3_SHIFT);
+	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
+	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
+	     SSB_SPROM8_TRI5G_SHIFT);
+	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
+	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
+	     SSB_SPROM8_TRI5GH_SHIFT);
+	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
+	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
+	     SSB_SPROM8_RXPO5G_SHIFT);
+	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
+	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
+	     SSB_SPROM8_RSSISMC2G_SHIFT);
+	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
+	     SSB_SPROM8_RSSISAV2G_SHIFT);
+	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
+	     SSB_SPROM8_BXA2G_SHIFT);
+	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
+	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
+	     SSB_SPROM8_RSSISMC5G_SHIFT);
+	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
+	     SSB_SPROM8_RSSISAV5G_SHIFT);
+	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
+	     SSB_SPROM8_BXA5G_SHIFT);
+	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
+	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
+	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
+	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
+	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
+	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
+	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
+	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
+	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
+	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
+	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
+	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
+	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
+	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
+	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
 
 	/* Extract the antenna gain values. */
 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01,
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index 5ae8fa2..17ffc1f 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -27,24 +27,54 @@ struct ssb_sprom {
 	u8 et1mdcport;		/* MDIO for enet1 */
 	u8 board_rev;		/* Board revision number from SPROM. */
 	u8 country_code;	/* Country Code */
-	u8 ant_available_a;	/* A-PHY antenna available bits (up to 4) */
-	u8 ant_available_bg;	/* B/G-PHY antenna available bits (up to 4) */
+	u8 ant_available_a;	/* 2GHz antenna available bits (up to 4) */
+	u8 ant_available_bg;	/* 5GHz antenna available bits (up to 4) */
 	u16 pa0b0;
 	u16 pa0b1;
 	u16 pa0b2;
 	u16 pa1b0;
 	u16 pa1b1;
 	u16 pa1b2;
+	u16 pa1lob0;
+	u16 pa1lob1;
+	u16 pa1lob2;
+	u16 pa1hib0;
+	u16 pa1hib1;
+	u16 pa1hib2;
 	u8 gpio0;		/* GPIO pin 0 */
 	u8 gpio1;		/* GPIO pin 1 */
 	u8 gpio2;		/* GPIO pin 2 */
 	u8 gpio3;		/* GPIO pin 3 */
-	u16 maxpwr_a;		/* A-PHY Amplifier Max Power (in dBm Q5.2) */
-	u16 maxpwr_bg;		/* B/G-PHY Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_bg;		/* 2.4GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_al;		/* 5.2GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_a;		/* 5.3GHz Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_ah;		/* 5.8GHz Amplifier Max Power (in dBm Q5.2) */
 	u8 itssi_a;		/* Idle TSSI Target for A-PHY */
 	u8 itssi_bg;		/* Idle TSSI Target for B/G-PHY */
-	u16 boardflags_lo;	/* Boardflags (low 16 bits) */
-	u16 boardflags_hi;	/* Boardflags (high 16 bits) */
+	u8 tri2g;		/* 2.4GHz TX isolation */
+	u8 tri5gl;		/* 5.2GHz TX isolation */
+	u8 tri5g;		/* 5.3GHz TX isolation */
+	u8 tri5gh;		/* 5.8GHz TX isolation */
+	u8 rxpo2g;		/* 2GHz RX power offset */
+	u8 rxpo5g;		/* 5GHz RX power offset */
+	u8 rssisav2g;		/* 2GHz RSSI params */
+	u8 rssismc2g;
+	u8 rssismf2g;
+	u8 bxa2g;		/* 2GHz BX arch */
+	u8 rssisav5g;		/* 5GHz RSSI params */
+	u8 rssismc5g;
+	u8 rssismf5g;
+	u8 bxa5g;		/* 5GHz BX arch */
+	u16 cck2gpo;		/* CCK power offset */
+	u32 ofdm2gpo;		/* 2.4GHz OFDM power offset */
+	u32 ofdm5glpo;		/* 5.2GHz OFDM power offset */
+	u32 ofdm5gpo;		/* 5.3GHz OFDM power offset */
+	u32 ofdm5ghpo;		/* 5.8GHz OFDM power offset */
+	u16 boardflags_lo;	/* Board flags (bits 0-15) */
+	u16 boardflags_hi;	/* Board flags (bits 16-31) */
+	u16 boardflags2_lo;	/* Board flags (bits 32-47) */
+	u16 boardflags2_hi;	/* Board flags (bits 48-63) */
+	/* TODO store board flags in a single u64 */
 
 	/* Antenna gain values for up to 4 antennas
 	 * on each band. Values in dBm/4 (Q5.2). Negative gain means the
@@ -58,7 +88,7 @@ struct ssb_sprom {
 		} ghz5;		/* 5GHz band */
 	} antenna_gain;
 
-	/* TODO - add any parameters needed from rev 2, 3, or 4 SPROMs */
+	/* TODO - add any parameters needed from rev 2, 3, 4, 5 or 8 SPROMs */
 };
 
 /* Information about the PCB the circuitry is soldered on. */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index a01b982..9ae9082 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -162,7 +162,7 @@
 
 /* SPROM shadow area. If not otherwise noted, fields are
  * two bytes wide. Note that the SPROM can _only_ be read
- * in two-byte quantinies.
+ * in two-byte quantities.
  */
 #define SSB_SPROMSIZE_WORDS		64
 #define SSB_SPROMSIZE_BYTES		(SSB_SPROMSIZE_WORDS * sizeof(u16))
@@ -327,8 +327,11 @@
 #define  SSB_SPROM5_GPIOB_P3_SHIFT	8
 
 /* SPROM Revision 8 */
-#define SSB_SPROM8_BFLLO		0x1084	/* Boardflags (low 16 bits) */
-#define SSB_SPROM8_BFLHI		0x1086	/* Boardflags Hi */
+#define SSB_SPROM8_BOARDREV		0x1082	/* Board revision */
+#define SSB_SPROM8_BFLLO		0x1084	/* Board flags (bits 0-15) */
+#define SSB_SPROM8_BFLHI		0x1086	/* Board flags (bits 16-31) */
+#define SSB_SPROM8_BFL2LO		0x1088	/* Board flags (bits 32-47) */
+#define SSB_SPROM8_BFL2HI		0x108A	/* Board flags (bits 48-63) */
 #define SSB_SPROM8_IL0MAC		0x108C	/* 6 byte MAC address */
 #define SSB_SPROM8_CCODE		0x1092	/* 2 byte country code */
 #define SSB_SPROM8_ANTAVAIL		0x109C  /* Antenna available bitfields*/
@@ -354,14 +357,63 @@
 #define  SSB_SPROM8_GPIOB_P2		0x00FF	/* Pin 2 */
 #define  SSB_SPROM8_GPIOB_P3		0xFF00	/* Pin 3 */
 #define  SSB_SPROM8_GPIOB_P3_SHIFT	8
-#define SSB_SPROM8_MAXP_BG		0x10C0  /* Max Power BG in path 1 */
-#define  SSB_SPROM8_MAXP_BG_MASK	0x00FF  /* Mask for Max Power BG */
+#define SSB_SPROM8_RSSIPARM2G		0x10A4	/* RSSI params for 2GHz */
+#define  SSB_SPROM8_RSSISMF2G		0x000F
+#define  SSB_SPROM8_RSSISMC2G		0x00F0
+#define  SSB_SPROM8_RSSISMC2G_SHIFT	4
+#define  SSB_SPROM8_RSSISAV2G		0x0700
+#define  SSB_SPROM8_RSSISAV2G_SHIFT	8
+#define  SSB_SPROM8_BXA2G		0x1800
+#define  SSB_SPROM8_BXA2G_SHIFT		11
+#define SSB_SPROM8_RSSIPARM5G		0x10A6	/* RSSI params for 5GHz */
+#define  SSB_SPROM8_RSSISMF5G		0x000F
+#define  SSB_SPROM8_RSSISMC5G		0x00F0
+#define  SSB_SPROM8_RSSISMC5G_SHIFT	4
+#define  SSB_SPROM8_RSSISAV5G		0x0700
+#define  SSB_SPROM8_RSSISAV5G_SHIFT	8
+#define  SSB_SPROM8_BXA5G		0x1800
+#define  SSB_SPROM8_BXA5G_SHIFT		11
+#define SSB_SPROM8_TRI25G		0x10A8	/* TX isolation 2.4&5.3GHz */
+#define  SSB_SPROM8_TRI2G		0x00FF	/* TX isolation 2.4GHz */
+#define  SSB_SPROM8_TRI5G		0xFF00	/* TX isolation 5.3GHz */
+#define  SSB_SPROM8_TRI5G_SHIFT		8
+#define SSB_SPROM8_TRI5GHL		0x10AA	/* TX isolation 5.2/5.8GHz */
+#define  SSB_SPROM8_TRI5GL		0x00FF	/* TX isolation 5.2GHz */
+#define  SSB_SPROM8_TRI5GH		0xFF00	/* TX isolation 5.8GHz */
+#define  SSB_SPROM8_TRI5GH_SHIFT	8
+#define SSB_SPROM8_RXPO			0x10AC  /* RX power offsets */
+#define  SSB_SPROM8_RXPO2G		0x00FF	/* 2GHz RX power offset */
+#define  SSB_SPROM8_RXPO5G		0xFF00	/* 5GHz RX power offset */
+#define  SSB_SPROM8_RXPO5G_SHIFT	8
+#define SSB_SPROM8_MAXP_BG		0x10C0  /* Max Power 2GHz in path 1 */
+#define  SSB_SPROM8_MAXP_BG_MASK	0x00FF  /* Mask for Max Power 2GHz */
 #define  SSB_SPROM8_ITSSI_BG		0xFF00	/* Mask for path 1 itssi_bg */
 #define  SSB_SPROM8_ITSSI_BG_SHIFT	8
-#define SSB_SPROM8_MAXP_A		0x10C8  /* Max Power A in path 1 */
-#define  SSB_SPROM8_MAXP_A_MASK		0x00FF  /* Mask for Max Power A */
+#define SSB_SPROM8_PA0B0		0x10C2	/* 2GHz power amp settings */
+#define SSB_SPROM8_PA0B1		0x10C4
+#define SSB_SPROM8_PA0B2		0x10C6
+#define SSB_SPROM8_MAXP_A		0x10C8  /* Max Power 5.3GHz */
+#define  SSB_SPROM8_MAXP_A_MASK		0x00FF  /* Mask for Max Power 5.3GHz */
 #define  SSB_SPROM8_ITSSI_A		0xFF00	/* Mask for path 1 itssi_a */
 #define  SSB_SPROM8_ITSSI_A_SHIFT	8
+#define SSB_SPROM8_MAXP_AHL		0x10CA  /* Max Power 5.2/5.8GHz */
+#define  SSB_SPROM8_MAXP_AH_MASK	0x00FF  /* Mask for Max Power 5.8GHz */
+#define  SSB_SPROM8_MAXP_AL_MASK	0xFF00  /* Mask for Max Power 5.2GHz */
+#define  SSB_SPROM8_MAXP_AL_SHIFT	8
+#define SSB_SPROM8_PA1B0		0x10CC	/* 5.3GHz power amp settings */
+#define SSB_SPROM8_PA1B1		0x10CE
+#define SSB_SPROM8_PA1B2		0x10D0
+#define SSB_SPROM8_PA1LOB0		0x10D2	/* 5.2GHz power amp settings */
+#define SSB_SPROM8_PA1LOB1		0x10D4
+#define SSB_SPROM8_PA1LOB2		0x10D6
+#define SSB_SPROM8_PA1HIB0		0x10D8	/* 5.8GHz power amp settings */
+#define SSB_SPROM8_PA1HIB1		0x10DA
+#define SSB_SPROM8_PA1HIB2		0x10DC
+#define SSB_SPROM8_CCK2GPO		0x1140	/* CCK power offset */
+#define SSB_SPROM8_OFDM2GPO		0x1142	/* 2.4GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GPO		0x1146	/* 5.3GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GLPO		0x114A	/* 5.2GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GHPO		0x114E	/* 5.8GHz OFDM power offset */
 
 /* Values for SSB_SPROM1_BINF_CCODE */
 enum {
-- 
1.6.2.4



^ permalink raw reply related

* Mesh fixes and improvements [v2]
From: Javier Cardona @ 2009-08-10 19:15 UTC (permalink / raw)
  To: linux-wireless; +Cc: andrey, johannes, linville, devel
In-Reply-To: <20090810175225.GF2733@tuxdriver.com>

[v2]

Withdraw "Simulate-transmission-losses-on-plinks-to-...".
Add detail to changelogs and Kconfig.
Changed queue_work -> ieee80211_queue_work

[v1]

This series includes some fixes, testing aids and improvements to the mesh
stack.  It is important to note that the patch
"Use-3-address-format-for-mesh-broadcast-frames" breaks compatibility with
previous versions.  This is unavoidable and will keep happening as new versions
of the 802.11s drafts are produced.

Also, I'm not sure if "Simulate-transmission-losses-on-plinks-to-..." should be
merged upstream, but we find it really useful to test mesh configurations.
Please comment if you have different opinions on its adequacy or
implementation.

Cheers,

Javier


 include/linux/nl80211.h         |    4 +
 include/net/cfg80211.h          |    4 +
 net/mac80211/Kconfig            |    8 +-
 net/mac80211/cfg.c              |    6 ++
 net/mac80211/ieee80211_i.h      |    2 +-
 net/mac80211/mesh.c             |  136 ++++++++++++++++++------------------
 net/mac80211/mesh.h             |   29 +++++++-
 net/mac80211/mesh_hwmp.c        |    9 +--
 net/mac80211/mesh_pathtbl.c     |  146 +++++++++++++++++++++++++++++----------
 net/mac80211/mesh_plink.c       |    6 ++
 net/mac80211/rc80211_minstrel.c |   16 ++++-
 net/mac80211/rx.c               |   45 ++++++++-----
 net/mac80211/sta_info.h         |    1 +
 net/mac80211/tx.c               |  118 +++++++++++++++++++++++---------
 net/wireless/nl80211.c          |    9 +++
 net/wireless/util.c             |   16 +++-
 16 files changed, 384 insertions(+), 171 deletions(-)


^ permalink raw reply

* [PATCH 1/7] mac80211: Improve dequeing from mpath frame queue.
From: Javier Cardona @ 2009-08-10 19:15 UTC (permalink / raw)
  To: linux-wireless; +Cc: Javier Cardona, andrey, johannes, linville, devel
In-Reply-To: <1249931752-18127-1-git-send-email-javier@cozybit.com>

Also, fix typo in comment.

Signed-off-by: Javier Cardona <javier@cozybit.com>
Tested-by: Andrey Yurovsky <andrey@cozybit.com>
---
 net/mac80211/mesh.h      |    2 +-
 net/mac80211/mesh_hwmp.c |    6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index ce53881..6aaf1ec 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -61,7 +61,7 @@ enum mesh_path_flags {
  * 	retry
  * @discovery_retries: number of discovery retries
  * @flags: mesh path flags, as specified on &enum mesh_path_flags
- * @state_lock: mesh pat state lock
+ * @state_lock: mesh path state lock
  *
  *
  * The combination of dst and sdata is unique in the mesh path table. Since the
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index e1a763e..b54c21c 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -810,10 +810,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
 		}
 
 		if (skb_queue_len(&mpath->frame_queue) >=
-				MESH_FRAME_QUEUE_LEN) {
-			skb_to_free = mpath->frame_queue.next;
-			skb_unlink(skb_to_free, &mpath->frame_queue);
-		}
+				MESH_FRAME_QUEUE_LEN)
+			skb_to_free = skb_dequeue(&mpath->frame_queue);
 
 		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
 		skb_queue_tail(&mpath->frame_queue, skb);
-- 
1.5.4.3


^ permalink raw reply related

* [PATCH 2/7] mac80211: Use correct sign for mesh active path refresh.
From: Javier Cardona @ 2009-08-10 19:15 UTC (permalink / raw)
  To: linux-wireless; +Cc: Andrey Yurovsky, Javier Cardona, johannes, linville, devel
In-Reply-To: <1249931752-18127-2-git-send-email-javier@cozybit.com>

From: Andrey Yurovsky <andrey@cozybit.com>

On locally originated traffic, we refresh active paths after a timeout.  The
decision to do this was using the wrong sign and therefore the refresh timer
was triggered for every frame.

Signed-off-by: Andrey Yurovsky <andrey@cozybit.com>
Signed-off-by: Javier Cardona <javier@cozybit.com>
---
 net/mac80211/mesh_hwmp.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index b54c21c..1cd1e72 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -791,7 +791,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
 	}
 
 	if (mpath->flags & MESH_PATH_ACTIVE) {
-		if (time_after(jiffies, mpath->exp_time -
+		if (time_after(jiffies, mpath->exp_time +
 			msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
 				&& !memcmp(sdata->dev->dev_addr, hdr->addr4,
 					   ETH_ALEN)
-- 
1.5.4.3


^ permalink raw reply related

* [PATCH 3/7] mac80211: Use 3-address format for mesh broadcast frames.
From: Javier Cardona @ 2009-08-10 19:15 UTC (permalink / raw)
  To: linux-wireless; +Cc: Javier Cardona, Andrey Yurovsky, johannes, linville, devel
In-Reply-To: <1249931752-18127-3-git-send-email-javier@cozybit.com>

The 11s task group recently changed the frame mesh multicast/broadcast frame
format to use 3-address.  This was done to avoid interactions with widely
deployed lazy-WDS access points.

This patch changes the format of group addressed frames, both mesh-originated
and proxied, to use the data format defined in draft D2.08 and forward.  The
address fields used for group addressed frames is:

In 802.11 header
 ToDS:0  FromDS:1
 addr1: DA  (broadcast/multicast address)
 addr2: TA
 addr3: Mesh SA

In address extension header:
 addr4: SA  (only present if frame was proxied)

Note that this change breaks backward compatibility with earlier mesh stack
versions.

Signed-off-by: Andrey Yurovsky <andrey@cozybit.com>
Signed-off-by: Javier Cardona <javier@cozybit.com>
---
 net/mac80211/mesh.c |   62 ++++++++++++++++++++++++++++++++++++++++++++++---
 net/mac80211/mesh.h |    5 +++-
 net/mac80211/rx.c   |   45 ++++++++++++++++++++++-------------
 net/mac80211/tx.c   |   64 +++++++++++++++++++++++++++------------------------
 net/wireless/util.c |   16 +++++++++---
 5 files changed, 136 insertions(+), 56 deletions(-)

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8c068e2..10d9338 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -399,21 +399,75 @@ endgrow:
 }
 
 /**
+ * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
+ * @hdr:    	802.11 frame header
+ * @fc:		frame control field
+ * @meshda:	destination address in the mesh
+ * @meshsa:	source address address in the mesh.  Same as TA, as frame is
+ *              locally originated.
+ *
+ * Return the length of the 802.11 (does not include a mesh control header)
+ */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char
+		*meshda, char *meshsa) {
+	if (is_multicast_ether_addr(meshda)) {
+		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+		/* DA TA SA */
+		memcpy(hdr->addr1, meshda, ETH_ALEN);
+		memcpy(hdr->addr2, meshsa, ETH_ALEN);
+		memcpy(hdr->addr3, meshsa, ETH_ALEN);
+		return 24;
+	} else {
+		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
+				IEEE80211_FCTL_TODS);
+		/* RA TA DA SA */
+		memset(hdr->addr1, 0, ETH_ALEN);   /* RA is resolved later */
+		memcpy(hdr->addr2, meshsa, ETH_ALEN);
+		memcpy(hdr->addr3, meshda, ETH_ALEN);
+		memcpy(hdr->addr4, meshsa, ETH_ALEN);
+		return 30;
+	}
+}
+
+/**
  * ieee80211_new_mesh_header - create a new mesh header
  * @meshhdr:    uninitialized mesh header
  * @sdata:	mesh interface to be used
+ * @addr4:	addr4 of the mesh frame (1st in ae header)
+ *              may be NULL
+ * @addr5:	addr5 of the mesh frame (1st or 2nd in ae header)
+ *              may be NULL unless addr6 is present
+ * @addr6:	addr6 of the mesh frame (2nd or 3rd in ae header)
+ * 		may be NULL unless addr5 is present
  *
  * Return the header length.
  */
 int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
-		struct ieee80211_sub_if_data *sdata)
+		struct ieee80211_sub_if_data *sdata, char *addr4,
+		char *addr5, char *addr6)
 {
-	meshhdr->flags = 0;
+	int aelen = 0;
+	memset(meshhdr, 0, sizeof(meshhdr));
 	meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
 	put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
 	sdata->u.mesh.mesh_seqnum++;
-
-	return 6;
+	if (addr4) {
+		meshhdr->flags |= MESH_FLAGS_AE_A4;
+		aelen += ETH_ALEN;
+		memcpy(meshhdr->eaddr1, addr4, ETH_ALEN);
+	}
+	if (addr5 && addr6) {
+		meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
+		aelen += 2 * ETH_ALEN;
+		if (!addr4) {
+			memcpy(meshhdr->eaddr1, addr5, ETH_ALEN);
+			memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
+		} else {
+			memcpy(meshhdr->eaddr2, addr5, ETH_ALEN);
+			memcpy(meshhdr->eaddr3, addr6, ETH_ALEN);
+		}
+	}
+	return 6 + aelen;
 }
 
 static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 6aaf1ec..2ebd74c 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -193,8 +193,11 @@ struct mesh_rmc {
 
 /* Public interfaces */
 /* Various */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
+		char *da, char *sa);
 int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
-		struct ieee80211_sub_if_data *sdata);
+		struct ieee80211_sub_if_data *sdata, char *addr4,
+		char *addr5, char *addr6);
 int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
 		struct ieee80211_sub_if_data *sdata);
 bool mesh_matches_local(struct ieee802_11_elems *ie,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 25a669c..e7d8895 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -489,12 +489,21 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
 	unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	char *dev_addr = rx->dev->dev_addr;
 
 	if (ieee80211_is_data(hdr->frame_control)) {
-		if (!ieee80211_has_a4(hdr->frame_control))
-			return RX_DROP_MONITOR;
-		if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
-			return RX_DROP_MONITOR;
+		if (is_multicast_ether_addr(hdr->addr1)) {
+			if (ieee80211_has_tods(hdr->frame_control) ||
+				!ieee80211_has_fromds(hdr->frame_control))
+				return RX_DROP_MONITOR;
+			if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0)
+				return RX_DROP_MONITOR;
+		} else {
+			if (!ieee80211_has_a4(hdr->frame_control))
+				return RX_DROP_MONITOR;
+			if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0)
+				return RX_DROP_MONITOR;
+		}
 	}
 
 	/* If there is not an established peer link and this is not a peer link
@@ -527,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 
 	if (ieee80211_is_data(hdr->frame_control) &&
 	    is_multicast_ether_addr(hdr->addr1) &&
-	    mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata))
+	    mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata))
 		return RX_DROP_MONITOR;
 #undef msh_h_get
 
@@ -1495,7 +1504,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 		/* illegal frame */
 		return RX_DROP_MONITOR;
 
-	if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
+	if (!is_multicast_ether_addr(hdr->addr1) &&
+			(mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) {
 		struct mesh_path *mppath;
 
 		rcu_read_lock();
@@ -1512,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 		rcu_read_unlock();
 	}
 
-	if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+	/* Frame has reached destination.  Don't forward */
+	if (!is_multicast_ether_addr(hdr->addr1) &&
+			compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
 		return RX_CONTINUE;
 
 	mesh_hdr->ttl--;
@@ -1532,22 +1544,21 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 						   rx->dev->name);
 
 			fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
-			/*
-			 * Save TA to addr1 to send TA a path error if a
-			 * suitable next hop is not found
-			 */
-			memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN);
 			memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
 			info = IEEE80211_SKB_CB(fwd_skb);
 			memset(info, 0, sizeof(*info));
 			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
 			info->control.vif = &rx->sdata->vif;
 			ieee80211_select_queue(local, fwd_skb);
-			if (is_multicast_ether_addr(fwd_hdr->addr3))
-				memcpy(fwd_hdr->addr1, fwd_hdr->addr3,
+			if (!is_multicast_ether_addr(fwd_hdr->addr1)) {
+				int err;
+				/*
+				 * Save TA to addr1 to send TA a path error if a
+				 * suitable next hop is not found
+				 */
+				memcpy(fwd_hdr->addr1, fwd_hdr->addr2,
 						ETH_ALEN);
-			else {
-				int err = mesh_nexthop_lookup(fwd_skb, sdata);
+				err = mesh_nexthop_lookup(fwd_skb, sdata);
 				/* Failed to immediately resolve next hop:
 				 * fwded frame was dropped or will be added
 				 * later to the pending skb queue.  */
@@ -1560,7 +1571,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 		}
 	}
 
-	if (is_multicast_ether_addr(hdr->addr3) ||
+	if (is_multicast_ether_addr(hdr->addr1) ||
 	    rx->dev->flags & IFF_PROMISC)
 		return RX_CONTINUE;
 	else
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c9be9dc..b9f5be6 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1415,9 +1415,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 
 	if (ieee80211_vif_is_mesh(&sdata->vif) &&
 	    ieee80211_is_data(hdr->frame_control)) {
-		if (is_multicast_ether_addr(hdr->addr3))
-			memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
-		else
+		if (!is_multicast_ether_addr(hdr->addr1))
 			if (mesh_nexthop_lookup(skb, sdata)) {
 				dev_put(sdata->dev);
 				return;
@@ -1620,52 +1618,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 		break;
 #ifdef CONFIG_MAC80211_MESH
 	case NL80211_IFTYPE_MESH_POINT:
-		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
 		if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
 			/* Do not send frames with mesh_ttl == 0 */
 			sdata->u.mesh.mshstats.dropped_frames_ttl++;
 			ret = 0;
 			goto fail;
 		}
-		memset(&mesh_hdr, 0, sizeof(mesh_hdr));
 
 		if (compare_ether_addr(dev->dev_addr,
 					  skb->data + ETH_ALEN) == 0) {
-			/* RA TA DA SA */
-			memset(hdr.addr1, 0, ETH_ALEN);
-			memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
-			memcpy(hdr.addr3, skb->data, ETH_ALEN);
-			memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
-			meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
+			hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+					skb->data, skb->data + ETH_ALEN);
+			meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
+					sdata, NULL, NULL, NULL);
 		} else {
 			/* packet from other interface */
 			struct mesh_path *mppath;
+			int is_mesh_mcast = 1;
+			char *mesh_da;
 
-			memset(hdr.addr1, 0, ETH_ALEN);
-			memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
-			memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
-
+			rcu_read_lock();
 			if (is_multicast_ether_addr(skb->data))
-				memcpy(hdr.addr3, skb->data, ETH_ALEN);
+				/* DA TA mSA AE:SA */
+				mesh_da = skb->data;
 			else {
-				rcu_read_lock();
 				mppath = mpp_path_lookup(skb->data, sdata);
-				if (mppath)
-					memcpy(hdr.addr3, mppath->mpp, ETH_ALEN);
-				else
-					memset(hdr.addr3, 0xff, ETH_ALEN);
-				rcu_read_unlock();
+				if (mppath) {
+					/* RA TA mDA mSA AE:DA SA */
+					mesh_da = mppath->mpp;
+					is_mesh_mcast = 0;
+				} else
+					/* DA TA mSA AE:SA */
+					mesh_da = dev->broadcast;
 			}
+			hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+					mesh_da, dev->dev_addr);
+			rcu_read_unlock();
+			if (is_mesh_mcast)
+				meshhdrlen =
+					ieee80211_new_mesh_header(&mesh_hdr,
+							sdata,
+							skb->data + ETH_ALEN,
+							NULL,
+							NULL);
+			else
+				meshhdrlen =
+					ieee80211_new_mesh_header(&mesh_hdr,
+							sdata,
+							NULL,
+							skb->data,
+							skb->data + ETH_ALEN);
 
-			mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6;
-			mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
-			put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum);
-			memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN);
-			memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN);
-			sdata->u.mesh.mesh_seqnum++;
-			meshhdrlen = 18;
 		}
-		hdrlen = 30;
 		break;
 #endif
 	case NL80211_IFTYPE_STATION:
diff --git a/net/wireless/util.c b/net/wireless/util.c
index ba387d8..693275a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -274,11 +274,11 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
 	switch (ae) {
 	case 0:
 		return 6;
-	case 1:
+	case MESH_FLAGS_AE_A4:
 		return 12;
-	case 2:
+	case MESH_FLAGS_AE_A5_A6:
 		return 18;
-	case 3:
+	case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6):
 		return 24;
 	default:
 		return 6;
@@ -333,10 +333,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
 		}
 		break;
 	case cpu_to_le16(IEEE80211_FCTL_FROMDS):
-		if (iftype != NL80211_IFTYPE_STATION ||
+		if ((iftype != NL80211_IFTYPE_STATION &&
+		    iftype != NL80211_IFTYPE_MESH_POINT) ||
 		    (is_multicast_ether_addr(dst) &&
 		     !compare_ether_addr(src, addr)))
 			return -1;
+		if (iftype == NL80211_IFTYPE_MESH_POINT) {
+			struct ieee80211s_hdr *meshdr =
+				(struct ieee80211s_hdr *) (skb->data + hdrlen);
+			hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+			if (meshdr->flags & MESH_FLAGS_AE_A4)
+				memcpy(src, meshdr->eaddr1, ETH_ALEN);
+		}
 		break;
 	case cpu_to_le16(0):
 		if (iftype != NL80211_IFTYPE_ADHOC)
-- 
1.5.4.3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox