Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Arjan van de Ven @ 2006-06-23 14:04 UTC (permalink / raw)
  To: Steve Wise; +Cc: rdreier, mshefty, linux-kernel, netdev, openib-general
In-Reply-To: <1151071005.7808.39.camel@stevo-desktop>

On Fri, 2006-06-23 at 08:56 -0500, Steve Wise wrote:
> On Fri, 2006-06-23 at 15:48 +0200, Arjan van de Ven wrote:
> > > > > +	/* Tell HW to xmit */
> > > > > +	__raw_writeq(cpu_to_be64(mapaddr), elem->hw_desc + C2_TXP_ADDR);
> > > > > +	__raw_writew(cpu_to_be16(maplen), elem->hw_desc + C2_TXP_LEN);
> > > > > +	__raw_writew(cpu_to_be16(TXP_HTXD_READY), elem->hw_desc + C2_TXP_FLAGS);
> > > > 
> > > > or here
> > > > 
> > > 
> > > No need here.  This logic submits the packet for transmission.  We don't
> > > assume it is transmitted until we (after a completion interrupt usually)
> > > read back the HTXD entry and see the TXP_HTXD_DONE bit set (see
> > > c2_tx_interrupt()). 
> > 
> > ... but will that interrupt happen at all if these 3 writes never hit
> > the hardware?
> > 
> 
> I thought the posted write WILL eventually get to adapter memory.  Not
> stall forever cached in a bridge.  I'm wrong?

I'm not sure there is a theoretical upper bound.... 

(and if it's several msec per bridge, then you have a lot of latency
anyway)


^ permalink raw reply

* Re: [RFC] sysfs + configfs on 802.11 wireless drivers
From: Luis R. Rodriguez @ 2006-06-23 14:13 UTC (permalink / raw)
  To: Jiri Benc; +Cc: netdev, John W. Linville, Greg KH
In-Reply-To: <20060623135233.724a8e51@griffin.suse.cz>

On 6/23/06, Jiri Benc <jbenc@suse.cz> wrote:
> On Thu, 22 Jun 2006 08:12:24 -0400, Luis R. Rodriguez wrote:
> > Kernel level:
> >
> > (1) Use the new *configfs* for all user-specific attributes
> > (2) Use *sysfs* read-only kobjects for device-specific attributes like
> > values which can be saved for suspend() and collected for resume().
> > IEEE 802.11 capabilities, features (for example radiotap), and what is
> > currently settable/gettable from private ioctl realm along with its
> > restrictions can also be exported via sysfs.
> > (3) On resume() talk to userspace via netlink to read our sysfs and configfs us
>
> We need a new hostapd<->stack communication protocol. It definitely
> cannot be configfs as we need asynchronous events; netlink seems to be
> the best solution for this. Why should be a part of 802.11
> userspace<->kernel communication done by netlink and part by
> configfs/sysfs?

I can see how a hostapd<-->stack communication should be done via
netlink. I wasn't looking for everything to be done via configfs but
just its configuration which parallels perfectly with the design goals
of configfs. I still think it would ease tuning and configuration for
userspace while being nice if used sysfs to export capabilities. A
simple tree on the sysfs tree for the wireless device would list all
the details for the device. Configf's usefulness here may become more
evident if you think in terms of tuning for radiotap -- disalbe/enable
a few bits here and there.

> Second, all new network stuff is configured via netlink (or by ioctls,
> but that doesn't count). Why should be 802.11 different?
>
> I'd rather choose the way of extending current WE-netlink.

Forget about the resume() idea then, what I suggest is just the
configuration of the drivers to be done via configfs while the
driver's capabilities listed via sysfs. I think this would be useful.

  Luis

^ permalink raw reply

* From Mr. MARK LEE
From: MRMARKLEE @ 2006-06-23 11:41 UTC (permalink / raw)
  To: mrmarklee

>From Mr. MARK LEE
LONDON, UK
E-MAIL:mrmarklee@micorreo.cai.es
ATN: CEO
Good Day!
I hope my email meets you well. I am in need of your assistance. My name is
Mr. MARK LEE. I am in charge of Administration, London Office of the
Engineering military unit, in Ba'qubah in Iraq, operating from our office in
London, UK. We have about $25 Million US dollars that we want to move out of
Iraq. My partner who is a south african citizen by name Dr p c smith,private
telephone +27 83 240 8038 and we
need a good partner that we can trust. This money is genareted from oil
and it is legal.

But the only way we can move the money is through diplomatic means,not to
london for security reason but to africa first before transferring to an 
bank of
your choice. The most important thing is can we trust you? Once the funds
get to you, you will take 20% of the total sum,while 75% will be for us.the
5% stands to cover all expenses.

Your own part of this deal is to give us all assistance needed
If you are interested I will furnish you with more
details. But the whole process is simple and we must keep a low profile at
all times.
I look forward to your reply and co-operation, and I thank you in
advance as I anticipate your co-operation. You can reach me on via email:
mrmarklee@micorreo.cai.es.
Waiting for your urgent response.
Regards,
Mr. MARK LEE

^ permalink raw reply

* Re: [RFC] sysfs + configfs on 802.11 wireless drivers
From: Luis R. Rodriguez @ 2006-06-23 14:20 UTC (permalink / raw)
  To: John W. Linville; +Cc: Dan Williams, netdev, Greg KH
In-Reply-To: <20060623124657.GA3256@tuxdriver.com>

On 6/23/06, John W. Linville <linville@tuxdriver.com> wrote:
> On Fri, Jun 23, 2006 at 07:50:47AM -0400, Dan Williams wrote:
>
> > Our effort is probably better spent elsewhere, especially since many
> > popular userspace tools already handle resume for us just fine.  More
> > useful to have drivers just come back from resume in an initialized
> > state similar to system boot.
>
> I think Dan makes a lot of sense here.  Does anyone disagree?

How would we handle resume() when using WPA, for example? Dan are you
suggesting we just bring up the driver clean and expect userspace to
have handled recovery from anything we were doing? This is important.

Or should we just expect the driver to re-set all settings again and
expect wpa_supplicant to pick up well where it left off? If sysfs will
get cleared after resume then there is no point in using it, but if
its stateful after suspend then this is where I suspected
wpa_supplicant could make use of sysfs. Perhaps there is a better way
-- what I suggested was just an idea while we discussed resume/suspend
in another thread. The reason I started a new thread was I figured
configfs/sysfs could be used for other things as well.

  Luis

^ permalink raw reply

* Re: [PATCH 0/2] NET: Accurate packet scheduling for ATM/ADSL
From: Patrick McHardy @ 2006-06-23 14:32 UTC (permalink / raw)
  To: hadi; +Cc: Jesper Dangaard Brouer, hawk, russell-tcatm, netdev,
	Stephen Hemminger
In-Reply-To: <1151001712.5392.47.camel@jzny2>

jamal wrote:
> On Tue, 2006-20-06 at 17:09 +0200, Patrick McHardy wrote:
> 
>>The thing I'm not sure about is whether this wouldn't be handled better
>>by userspace, 
> 
> 
> If you do it in user space you will need a daemon of some form; this is
> my preference but it seems a lot of people hate daemons - the standard
> claim is it is counter-usability. Such people are forgiving if you built
> the daemon into the kernel as a thread. Perhaps the netcarrier that
> Stefan Rompf has added could be extended to handle this)

I absolutely disagree, in my opinion we currently have too few
specialized daemons and a lot too much wild shell scripting with
seding, awking, grepping. I'm actually working on a daemon that
provides a small scripting language to express networking configuration
states and react to result set changes and state transitions.
Its far from complete, but already allows you to express things like
"on transition from { none, link flags !up } to { link flags up }
execute /etc/network/link/$LINK_NAME up" (same for routes, addresses,
basically all object types) or "for each link with flags lowerup,up
execute add-to-bridge.sh". The value of each expression is dumped
into the environment on execution, so you can comfortably use
$LINK_NAME or $LINK_MTU instead of having to cut it out the
"ip link list" output. Should be trivial to support link speed changes
once we have notifications for that.

>>if the link layer speed changes you might not want
>>proportional scaling but prefer to still give a fixed amount of that
>>bandwidth to some class, for example VoIP traffic. Do we have netlink
>>notifications for link speed changes?
> 
> 
> Not there at the moment - but we do emit event for other link layer
> stuff like carrier on/off - so adding this should be trivial and a
> reasonable thing to have; with a caveat: it will be link-layer specific;
> so whoever ends up adding will have to be careful to make sure it is not
> hard-coded to be specific to ethernet-like netdevices. It could probably
> be reported together with link state as a TLV like ETHER_SPEED_CHANGED
> which carries probably old speed and new speed
> and maybe even reason why it changed.

I don't think it should carry both old and new speed. Netlink
notifications usually provide a snapshot of the new state, but
no indication what changed, with one notable exception, the
ifi_change field, which IMO is a hack for lazy userspace. Since
notifications can get lost, userspace needs to resync occasionally.
The naiive approach (works for every other object) to determine if
the object state changed from my last known state is to compare
all attributes .. but the ifi_change field will be different
between notifications and dumps even if the object itself didn't
change. "Lazy userspace" because looking at ifi_change is obviously
only useful if it doesn't keep its last known state and tries to
derive the change from update notifications alone .. which means it
fails when notifications are lost.


^ permalink raw reply

* Re: [RFC] sysfs + configfs on 802.11 wireless drivers
From: Dan Williams @ 2006-06-23 14:37 UTC (permalink / raw)
  To: Luis R. Rodriguez; +Cc: John W. Linville, netdev, Greg KH
In-Reply-To: <43e72e890606230720ta1dc2cm6c9871a71f3872f0@mail.gmail.com>

On Fri, 2006-06-23 at 10:20 -0400, Luis R. Rodriguez wrote:
> On 6/23/06, John W. Linville <linville@tuxdriver.com> wrote:
> > On Fri, Jun 23, 2006 at 07:50:47AM -0400, Dan Williams wrote:
> >
> > > Our effort is probably better spent elsewhere, especially since many
> > > popular userspace tools already handle resume for us just fine.  More
> > > useful to have drivers just come back from resume in an initialized
> > > state similar to system boot.
> >
> > I think Dan makes a lot of sense here.  Does anyone disagree?
> 
> How would we handle resume() when using WPA, for example? Dan are you
> suggesting we just bring up the driver clean and expect userspace to
> have handled recovery from anything we were doing? This is important.

Well, for WPA you pretty much need a tool like wpa_supplicant to handle
connections for you, otherwise there wouldn't be a reason for
wpa_supplicant to exist.  I don't think we really want a bunch of code
in the driver to handle the reconnect cases, because this is exactly
what wpa_supplicant already does and does well.

I'm suggesting that instead of trying to stuff more logic in the drivers
to handle reconnect on resume, that the drivers just come back from
resume in the same state as system boot.  They are initialized with
default values and default state.  They allow userspace tools that
already know about policy and security to configure them, as these tools
would do right after a system boot.  We can't assume that policy and
security are exactly the same as before sleep.

> Or should we just expect the driver to re-set all settings again and
> expect wpa_supplicant to pick up well where it left off? If sysfs will
> get cleared after resume then there is no point in using it, but if
> its stateful after suspend then this is where I suspected
> wpa_supplicant could make use of sysfs. Perhaps there is a better way
> -- what I suggested was just an idea while we discussed resume/suspend
> in another thread. The reason I started a new thread was I figured
> configfs/sysfs could be used for other things as well.

I don't know that you can really "pick up where it left off" in many
cases.  Again, the system can be moved, access points can go away, more
networks can appear, and WPA is just more complicated than WEP.  You
simply cannot expect a driver to return to its exact pre-sleep state
when using stuff like 802.1x/WPA[12] Enterprise.  What if your session
keys expired while you were sleeping, for example?

Besides, userspace tools like NetworkManager and wpa_supplicant are just
going to overwrite the config settings the driver restores anyway, since
you need to do another scan, check results, and set the connection back
up after accounting for stuff that may have changed.  Blindly attempting
to connect with all the old settings doesn't make much sense.

It's an optimization to have the driver restore settings, and it might
make things a second or three quicker in some cases, but in many other
cases it's completely useless to do this, possibly detrimental.  I don't
see the overall benefit right now.  We've got better things to do with
drivers at the moment.

Dan



^ permalink raw reply

* Re: [PATCH 2/2] NET: Accurate packet scheduling for ATM/ADSL (userspace)
From: Patrick McHardy @ 2006-06-23 15:05 UTC (permalink / raw)
  To: hadi
  Cc: Jesper Dangaard Brouer, netdev, Stephen Hemminger, Alan Cox,
	Russell Stuart
In-Reply-To: <1151002931.5392.69.camel@jzny2>

jamal wrote:
> On Tue, 2006-20-06 at 18:51 +0200, Patrick McHardy wrote:
> 
>> [..]
>>
>>contrary to a local link that would be best managed
>>in work-conserving mode. And I think for better accuracy it is
>>necessary to manage effective throughput, especially if you're
>>interested in guaranteed delays.
>>
> 
> 
> Indeed - but "fixing" the scheduler to achieve such management is not
> the first choice (would be fine if it is generic and non-intrusive)

I have a patch that introduces "sizetables" similar to ratetables
and performs the mapping once and stores the calculated size in the
cb. The schedulers take the size from the cb. Its not very large and
only has minimum overhead. I got distracted during testing by
inaccuracies in the 100mbit range with small packets caused by the
clock source resolution, so I've added a ktime() clocksource and am
currently busy auditing for integer overflows caused by the increased
clock rate. I'll clean up the patch once I'm done with that and post it.

>> [..]
>>
>>I think that point can be used to argue in favour of that Linux should
>>be able to manage effective throughput :)
>>
> 
> I think you have convinced me this is valuable I even suggest probes
> above to discover goodput;-). I hope i have convinced you how rude it
> would be to make extensive changes to compensate for goodput;->

Sure :) So far I haven't been able to measure any improvement by
accounting for link layer overhead, but probably because my test
scenario was chosen badly (very small overhead, large speed) and the
differences were lost in the noise.

>>>I am saying that #2 is the choice to go with hence my assertion earlier,
>>>it should be fine to tell the scheduler all it has is 1Mbps and nobody
>>>gets hurt. #1 if i could do it with minimal intrusion and still get to
>>>use it when i have 802.11g. 
>>>
>>>Not sure i made sense.
>>
>>HFSC is actually capable of handling this quite well. If you use it
>>in work-conserving mode (and the card doesn't do (much) internal
>>queueing) it will get clocked by successful transmissions. Using
>>link-sharing classes you can define proportions for use of available
>>bandwidth, possibly with upper limits. No hacks required :)
>>
> 
> 
> HFSC sounds very interesting - I should go and study it a little more.
> My understanding is though that it is a bit of a CPU pig, true?

It does more calculations at runtime than token-bucket based schedulers,
but it does perform comparable to HTB with a large number of classes,
in which case the constant overhead is probably not visible anymore
because much more time is spent searching, walking lists and trees and
so on. I didn't do any comparisons of constant costs.

>>Anyway, this again goes more in the direction of handling link speed
>>changes.
>>
> 
> 
> The more we discuss this, the more i think they are the same thing ;->

Not really. Link speed changes can be expressed by constant factors
that apply to bandwidth and delay (bandwidth *= f, delay /= f). Link
layer overhead usually can't be expressed this way.

>>>ip dev add compensate_header 100 bytes
>>
>>[...]
>>
>>Unforunately I can't think of a way to handle the ATM case without
>>a division .. or iteration.
>
> 
> I am not thinking straight right now but it does not sound like a big
> change to me i.e within reason.

I also got rid of the division ..

> Note, it may be valuable to think of
> this as related to the speed changing daemon as i stated earlier.
> Only in this case it is "static" discovery of link layer
> goodput/throughput vs some other way to dynamically discover things. 

I still think these are two quite different things. Link speed
changes also can't be handled very well by scaling packet sizes
since TBF-based qdiscs have configured maxima for the packet sizes
they can handle.


^ permalink raw reply

* Re: [RFC] sysfs + configfs on 802.11 wireless drivers
From: Johannes Berg @ 2006-06-23 15:12 UTC (permalink / raw)
  To: Jiri Benc; +Cc: Luis R. Rodriguez, netdev, John W. Linville, Greg KH
In-Reply-To: <20060623135233.724a8e51@griffin.suse.cz>

[-- Attachment #1: Type: text/plain, Size: 219 bytes --]

On Fri, 2006-06-23 at 13:52 +0200, Jiri Benc wrote:

> I'd rather choose the way of extending current WE-netlink.

Nah, let's rewrite the blob of magic. Well, at least that's how I see it
right now ;)

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 793 bytes --]

^ permalink raw reply

* Re: [PATCH 0/2] NET: Accurate packet scheduling for ATM/ADSL
From: Patrick McHardy @ 2006-06-23 15:21 UTC (permalink / raw)
  To: Russell Stuart
  Cc: hadi, Alan Cox, Stephen Hemminger, netdev, Jesper Dangaard Brouer
In-Reply-To: <1151066247.4217.254.camel@ras.pc.brisbane.lube>

Russell Stuart wrote:
> On Thu, 2006-06-22 at 14:29 -0400, jamal wrote: 
> 

> On Tue, 2006-06-20 at 03:04 +0200, Patrick McHardy wrote: 
> 
>>What about qdiscs like SFQ (which uses the packet size in quantum
>>calculations)? I guess it would make sense to use the wire-length
>>there as well.
> 
> 
> Being pedantic, SQF automatically assigns traffic to classes 
> and gives each class an equal share of the available bandwidth.  
> As I am sure you are aware SQF's trick is that it randomly 
> changes its classification algorithm - every second in the Linux 
> implementation.  If there are errors in rate calculation this 
> randomisation will ensure they are distributed equally between 
> the classes as time goes on.  So no, accurate packets sizes are 
> not that important to SQF.
> 
> But they are important to many other qdiscs, and I am sure 
> that was your point.  SQF just happened to be a bad example.

Not really. The randomization doesn't happen by default, but it doesn't
influence this anyway. SFQ allows flows to send up to "quantum" bytes
at a time before moving on to the next one. A flow that sends 75 * 20
byte will in the eyes of SFQ use 1500bytes, on the (ethernet) wire it
needs 4800bytes. A flow that sents 1500byte packets will only need
1504 bytes on the wire, but will be treated equally. So it does make
a different for SFQ.

> On Tue, 2006-06-20 at 16:45 +0200, Patrick McHardy wrote: 
> 
>>Handling all qdiscs would mean adding a pointer to a mapping table
>>to struct net_device and using something like "skb_wire_len(skb, dev)"
>>instead of skb->len in the queueing layer. That of course doesn't
>>mean that we can't still provide pre-adjusted ratetables for qdiscs
>>that use them.
> 
> 
> Yes, that would work well, and is probably how it should of
> been done when the kernel stuff was originally written.  As 
> it happens Jesper's original solution was closer to this.  The 
> reason we choose not to go that way it is would change the 
> kernel-userspace API.   The current patch solves the problem 
> and works well as possible on all kernel versions - both 
> patched and unpatched.

Not a problem as long as the new stuff doesn't break anything existing.
My patch introduces a TCA_STAB (for size table), similar to the _RTAB
attributes. Old iproute with new kernel and new iproute with old kernel
both work fine.

> Now that I think about to change things the way you suggest
> here does seem simple enough.  But it probably belongs in a 
> different patch.  We wrote this patch to fix a specific problem 
> with ATM links, and it should succeed or fail on the merits 
> of doing that.  Cleaning up the kernel code to do what you 
> suggest is a different issue.  Let whether it to should be 
> done, or not, be based on its own merits.

Its not about cleanup, its about providing the same capabilities
to all qdiscs instead of just a few selected ones and generalizing
it so it is also usable for non-ATM overhead calculations.

^ permalink raw reply

* RE: New Qlogic qla3xxx NIC Driver v2.02.00k34 for upstream inclusion
From: Ron Mercer @ 2006-06-23 16:36 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, Linux Driver

Jeff,

I will address all of your points in my next post. 

> 5) What is the point of using the hardware semaphore?  Is a firmware 
> competing with the device driver somehow, and its activities require 
> synchronization with the OS driver?

>From the body of my previous post:
- ql_wait_for_drvr_lock is used to protect resources that are shared 
  across the network and iSCSI functions.  This protection is mostly 
  during chip initialization and resets, but also include link 
  management.


The drvr_lock is to allow the iSCSI driver, iSCSI firmware, and the
network driver exclusive access to common points in the hardware.


> 12) For new drivers, we see additional work and little value for 
> maintained #ifdef'd NAPI and non-NAPI code paths.  Just pick the best 
> one (and justify that decision with sound technical rationale).

I am not sure what sound technical rationale would apply here.  NAPI has
better thoughput and non-NAPI has lower latency.  However, I will pick
one and remove the other.






^ permalink raw reply

* [PATCH]: e1000: Janitor: Use #defined values for literals
From: Linas Vepstas @ 2006-06-23 16:36 UTC (permalink / raw)
  To: netdev, john.ronciak, jesse.brandeburg, jeffrey.t.kirsher,
	Zhang, Yanmin
  Cc: Jeff Garzik, linux-kernel


Minor janitorial patch: use #defines for literal values.

Signed-off-by: Linas Vepstas <linas@austin.ibm.com>

----
 drivers/net/e1000/e1000_main.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

Index: linux-2.6.17-rc6-mm2/drivers/net/e1000/e1000_main.c
===================================================================
--- linux-2.6.17-rc6-mm2.orig/drivers/net/e1000/e1000_main.c	2006-06-13 18:13:30.000000000 -0500
+++ linux-2.6.17-rc6-mm2/drivers/net/e1000/e1000_main.c	2006-06-23 11:27:47.000000000 -0500
@@ -4663,8 +4663,8 @@ static pci_ers_result_t e1000_io_slot_re
 	}
 	pci_set_master(pdev);
 
-	pci_enable_wake(pdev, 3, 0);
-	pci_enable_wake(pdev, 4, 0); /* 4 == D3 cold */
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_enable_wake(pdev, PCI_D3cold, 0);
 
 	/* Perform card reset only on one instance of the card */
 	if (PCI_FUNC (pdev->devfn) != 0)

^ permalink raw reply

* [PATCH wireless-dev] add adm8211 driver
From: Michael Wu @ 2006-06-23 16:51 UTC (permalink / raw)
  To: linville; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 90512 bytes --]

(resend, please merge)

This patch adds support for the ADMtek ADM8211 card. It is not completed yet, 
but I feel the code is clean enough to merge into wireless-dev. It currently 
supports STA mode.

Signed-off-by: Michael Wu <flamingice@sourmilk.net>

diff --git a/drivers/net/wireless/d80211/Kconfig 
b/drivers/net/wireless/d80211/Kconfig
index 972c473..3656ee5 100644
--- a/drivers/net/wireless/d80211/Kconfig
+++ b/drivers/net/wireless/d80211/Kconfig
@@ -1,2 +1,3 @@
 source "drivers/net/wireless/d80211/bcm43xx/Kconfig"
 source "drivers/net/wireless/d80211/rt2x00/Kconfig"
+source "drivers/net/wireless/d80211/adm8211/Kconfig"
diff --git a/drivers/net/wireless/d80211/Makefile 
b/drivers/net/wireless/d80211/Makefile
index 32c5c8d..e491a41 100644
--- a/drivers/net/wireless/d80211/Makefile
+++ b/drivers/net/wireless/d80211/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_BCM43XX_D80211)	+= bcm43xx/
 obj-$(CONFIG_RT2X00)		+= rt2x00/
+obj-$(CONFIG_ADM8211)		+= adm8211/
diff --git a/drivers/net/wireless/d80211/adm8211/Kconfig 
b/drivers/net/wireless/d80211/adm8211/Kconfig
new file mode 100644
index 0000000..9846cdf
--- /dev/null
+++ b/drivers/net/wireless/d80211/adm8211/Kconfig
@@ -0,0 +1,24 @@
+config ADM8211
+	tristate "ADMtek ADM8211 support"
+	depends on NET_RADIO && PCI && D80211 && EXPERIMENTAL
+	---help---
+	  This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
+	  These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as:
+
+	  Xterasys Cardbus XN-2411b
+	  Blitz NetWave Point PC
+	  TrendNet 221pc
+	  Belkin F5D6001
+	  SMC 2635W
+	  Linksys WPC11 v1
+	  Fiberline FL-WL-200X
+	  3com Office Connect (3CRSHPW796)
+	  Corega WLPCIB-11
+	  SMC 2602W V2 EU
+	  D-Link DWL-520 Revision C
+
+	  However, some of these cards have been replaced with other chips
+	  like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
+	  the Ralink RT2400 (SMC2635W) without a model number change.
+
+	  Thanks to Infineon-ADMtek for their support of this driver.
diff --git a/drivers/net/wireless/d80211/adm8211/Makefile 
b/drivers/net/wireless/d80211/adm8211/Makefile
new file mode 100644
index 0000000..9cca7e5
--- /dev/null
+++ b/drivers/net/wireless/d80211/adm8211/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ADM8211)	+= adm8211.o
diff --git a/drivers/net/wireless/d80211/adm8211/adm8211.c 
b/drivers/net/wireless/d80211/adm8211/adm8211.c
new file mode 100644
index 0000000..c766c4e
--- /dev/null
+++ b/drivers/net/wireless/d80211/adm8211/adm8211.c
@@ -0,0 +1,2202 @@
+
+/*
+ * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP)
+ *
+ * Copyright (c) 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004-2006, Michael Wu <flamingice@sourmilk.net>
+ * Some parts copyright (c) 2003 by David Young <dyoung@pobox.com>
+ * and used with permission.
+ *
+ * Much thanks to Infineon-ADMtek for their support of this driver.
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/wireless.h>
+#include <net/d80211.h>
+#include <asm/delay.h>
+#include <asm/unaligned.h>
+#include <asm/types.h>
+#include <asm/div64.h>
+
+#include "adm8211.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>, Jouni Malinen 
<jkmaline@cc.hut.fi>");
+MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless LAN cards based on 
ADMtek ADM8211");
+MODULE_SUPPORTED_DEVICE("ADM8211");
+MODULE_LICENSE("GPL");
+
+static unsigned int tx_ring_size = 16;
+static unsigned int rx_ring_size = 16;
+static int debug = 1;
+
+module_param(tx_ring_size, uint, 0);
+module_param(rx_ring_size, uint, 0);
+module_param(debug, int, 0);
+
+static const char *version = KERN_INFO "adm8211: " 
+"Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>; "
+"Copyright 2004-2006, Michael Wu <flamingice@sourmilk.net>\n";
+
+
+static struct pci_device_id adm8211_pci_id_table[] __devinitdata = {
+	/* ADMtek ADM8211 */
+	{ 0x10B7, 0x6000, PCI_ANY_ID, PCI_ANY_ID }, /* 3Com 3CRSHPW796 */
+	{ 0x1200, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ? */
+	{ 0x1317, 0x8201, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211A */
+	{ 0x1317, 0x8211, PCI_ANY_ID, PCI_ANY_ID }, /* ADM8211B/C */
+	{ 0 }
+};
+
+#define ADM8211_INTMASK \
+(ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | \
+ADM8211_IER_TDUIE | ADM8211_IER_GPTIE)
+
+#define PLCP_SIGNAL_1M		0x0a
+#define PLCP_SIGNAL_2M		0x14
+#define PLCP_SIGNAL_5M5		0x37
+#define PLCP_SIGNAL_11M		0x6e
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],
((u8*)(x))[4],((u8*)(x))[5]
+
+struct adm8211_tx_hdr {
+	u8 da[6];
+	u8 signal; /* PLCP signal / TX rate in 100 Kbps */
+	u8 service;
+	__le16 frame_body_size;
+	__le16 frame_control;
+	__le16 plcp_frag_tail_len;
+	__le16 plcp_frag_head_len;
+	__le16 dur_frag_tail;
+	__le16 dur_frag_head;
+	u8 addr4[6];
+
+#define ADM8211_TXHDRCTL_SHORT_PREAMBLE		(1 <<  0)
+#define ADM8211_TXHDRCTL_MORE_FRAG		(1 <<  1)
+#define ADM8211_TXHDRCTL_MORE_DATA		(1 <<  2)
+#define ADM8211_TXHDRCTL_FRAG_NO		(1 <<  3) /* ? */
+#define ADM8211_TXHDRCTL_ENABLE_RTS		(1 <<  4)
+#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE	(1 <<  5)
+#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER	(1 << 15) /* ? */
+	__le16 header_control;
+	__le16 frag;
+	u8 reserved_0;
+	u8 retry_limit;
+
+	u32 wep2key0;
+	u32 wep2key1;
+	u32 wep2key2;
+	u32 wep2key3;
+
+	u8 keyid;
+	u8 entry_control;	// huh??
+	u16 reserved_1;
+	u32 reserved_2;
+} __attribute__ ((packed));
+
+
+#define RX_COPY_BREAK 128
+#define RX_PKT_SIZE 2500
+
+/* Serial EEPROM reading for 93C66/93C46 */
+#define EE_ENB		(0x4000 | ADM8211_SPR_SRS | ADM8211_SPR_SCS)
+#define EE_READ_CMD	(6)
+#define eeprom_delay()	ADM8211_CSR_READ(SPR);
+
+
+static u16 adm8211_eeprom_read_word(struct net_device *dev, unsigned int 
addr,
+				    unsigned int addr_len)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int read_cmd = addr | (EE_READ_CMD << addr_len);
+	int i;
+	u16 retval = 0;
+
+	ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS));
+	eeprom_delay();
+	ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
+	eeprom_delay();
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		u32 dataval = EE_ENB | ((read_cmd & (1 << i)) ? ADM8211_SPR_SDI : 0);
+		ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval));
+		eeprom_delay();
+		ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval | ADM8211_SPR_SCLK));
+		eeprom_delay();
+	}
+
+	ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
+	eeprom_delay();
+
+	for (i = 16; i > 0; i--) {
+		ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB | ADM8211_SPR_SCLK));
+		eeprom_delay();
+		retval <<= 1;
+		if (ADM8211_CSR_READ(SPR) & __constant_cpu_to_le32(ADM8211_SPR_SDO))
+			retval |= 1;
+		ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB));
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS));
+
+	return retval;
+}
+
+
+static int adm8211_read_eeprom(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int addr_len, words, i;
+	struct ieee80211_chan_range chan_range;
+	u16 cr49;
+
+	if (ADM8211_CSR_READ(CSR_TEST0) & __constant_cpu_to_le32
(ADM8211_CSR_TEST0_EPTYP)) {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type: 93C66\n", 
pci_name(priv->pdev));
+		/* 256 * 16-bit = 512 bytes */
+		addr_len = 8;
+		words = 256;
+	} else {
+		printk(KERN_DEBUG "%s (adm8211): EEPROM type 93C46\n", 
pci_name(priv->pdev));
+		/* 64 * 16-bit = 128 bytes */
+		addr_len = 6;
+		words = 64;
+	}
+
+	priv->eeprom_len = words * 2;
+	priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL);
+	if (priv->eeprom == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < words; i++)
+		*((u16 *) &((u8 *)priv->eeprom)[i * 2]) =
+			adm8211_eeprom_read_word(dev, i, addr_len);
+
+	cr49 = le16_to_cpu(priv->eeprom->cr49);
+	priv->rf_type = (cr49 >> 3) & 0x7;
+	switch (priv->rf_type) {
+	case ADM8211_TYPE_INTERSIL:
+	case ADM8211_TYPE_RFMD:
+	case ADM8211_TYPE_MARVEL:
+	case ADM8211_TYPE_AIROHA:
+	case ADM8211_TYPE_ADMTEK:
+		break;
+	
+	default:
+		if (priv->revid < ADM8211_REV_CA)
+			priv->rf_type = ADM8211_TYPE_RFMD;
+		else
+			priv->rf_type = ADM8211_TYPE_AIROHA;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported RFtype: %d, 
assuming %d\n",
+		       pci_name(priv->pdev), (cr49 >> 3) & 0x7, priv->rf_type);
+	}
+
+	priv->bbp_type = cr49 & 0x7;
+	switch (priv->bbp_type) {
+	case ADM8211_TYPE_INTERSIL:
+	case ADM8211_TYPE_RFMD:
+	case ADM8211_TYPE_MARVEL:
+	case ADM8211_TYPE_AIROHA:
+	case ADM8211_TYPE_ADMTEK:
+		break;
+	
+	default:
+		if (priv->revid < ADM8211_REV_CA)
+			priv->bbp_type = ADM8211_TYPE_RFMD;
+		else
+			priv->bbp_type = ADM8211_TYPE_ADMTEK;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported BBPtype: %d, 
assuming %d\n",
+		       pci_name(priv->pdev), cr49 >> 3, priv->bbp_type);
+	}
+
+	if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid country code (%d) in EEPROM, 
assuming ETSI\n",
+		       pci_name(priv->pdev), priv->eeprom->country_code);
+
+		chan_range = cranges[2];
+	} else
+		chan_range = cranges[priv->eeprom->country_code];
+
+	printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n",
+	       pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max);
+
+	priv->modes[0].num_channels = chan_range.max - chan_range.min + 1;
+	priv->modes[0].channels = kmalloc(priv->modes[0].num_channels * 
sizeof(struct ieee80211_channel), GFP_KERNEL);
+	if (priv->modes[0].channels == NULL) {
+		kfree(priv->eeprom);
+		return -ENOMEM;
+	}
+
+	memcpy(priv->modes[0].channels, &adm8211_channels[chan_range.min-1],
+		priv->modes[0].num_channels * sizeof(struct ieee80211_channel));
+
+	switch (priv->eeprom->specific_bbptype) {
+	case ADM8211_BBP_RFMD3000:
+	case ADM8211_BBP_RFMD3002:
+	case ADM8211_BBP_ADM8011:
+		priv->specific_bbptype = priv->eeprom->specific_bbptype;
+		break;
+	
+	default:
+		if (priv->revid < ADM8211_REV_CA)
+			priv->specific_bbptype = ADM8211_BBP_RFMD3000;
+		else
+			priv->specific_bbptype = ADM8211_BBP_ADM8011;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported specific BBP: %d, 
assuming %d\n",
+		       pci_name(priv->pdev), priv->eeprom->specific_bbptype, 
priv->specific_bbptype);
+	}
+	
+	switch (priv->eeprom->specific_rftype) {
+	case ADM8211_RFMD2948:
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+	case ADM8211_MAX2820:
+	case ADM8211_AL2210L:
+		priv->transceiver_type = priv->eeprom->specific_rftype;
+		break;
+
+	default:
+		if (priv->revid == ADM8211_REV_BA)
+			priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER;
+		else if (priv->revid == ADM8211_REV_CA)
+			priv->transceiver_type = ADM8211_AL2210L;
+		else if (priv->revid == ADM8211_REV_AB)
+			priv->transceiver_type = ADM8211_RFMD2948;
+
+		printk(KERN_WARNING "%s (adm8211): Invalid or unsupported transceiver: %d, 
assuming %d\n",
+		       pci_name(priv->pdev), priv->eeprom->specific_rftype, 
priv->transceiver_type);
+
+		break;
+	}
+
+	printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d 
Transceiver=%d\n",
+	       pci_name(priv->pdev), priv->rf_type, priv->bbp_type,
+	       priv->specific_bbptype, priv->transceiver_type);
+
+	return 0;
+}
+
+static inline void adm8211_write_sram(struct net_device *dev, u32 addr, 
__le32 data)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(addr | ADM8211_WEPCTL_TABLE_WR |
+			  (priv->revid < ADM8211_REV_BA ?
+			   0 : ADM8211_WEPCTL_SEL_WEPTABLE )) );
+	ADM8211_CSR_READ(WEPCTL);
+	mdelay(1);
+
+	ADM8211_CSR_WRITE(WESK, data);
+	ADM8211_CSR_READ(WESK);
+	mdelay(1);
+}
+
+static void adm8211_write_sram_bytes(struct net_device *dev,
+				     unsigned int addr, u8 *buf, unsigned int len)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	__le32 reg = ADM8211_CSR_READ(WEPCTL);
+	unsigned int i;
+
+	if (priv->revid < ADM8211_REV_BA) {
+		for (i = 0; i < len; i += 2) {
+			u16 val = buf[i] | buf[i + 1] << 8;
+			adm8211_write_sram(dev, addr + i / 2, cpu_to_le32(val));
+		}
+	} else {
+		for (i = 0; i < len; i += 4) {
+			u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) |
+				  (buf[i + 2] << 16) | (buf[i + 3] << 24);
+			adm8211_write_sram(dev, addr + i / 4, cpu_to_le32(val));
+		}
+	}
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static void adm8211_clear_sram(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	__le32 reg = ADM8211_CSR_READ(WEPCTL);
+	unsigned int addr;
+
+	for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++)
+			adm8211_write_sram(dev, addr, 0);
+
+	ADM8211_CSR_WRITE(WEPCTL, reg);
+}
+
+static int adm8211_get_stats(struct net_device *dev,
+			     struct ieee80211_low_level_stats *stats)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	memcpy(stats, &priv->stats, sizeof(*stats));
+
+	return 0;
+}
+
+static void adm8211_set_rx_mode(struct net_device *dev,
+				unsigned short flags, int mc_count)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int bit_nr;
+	__le32 mc_filter[2];
+	struct dev_mc_list *mclist;
+	void *tmp;
+
+		mc_filter[1] = mc_filter[0] = 0;
+	if (flags & IFF_PROMISC) {
+		priv->nar |= ADM8211_NAR_PR;
+		priv->nar &= ~ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else if ((flags & IFF_ALLMULTI) || (mc_count > -1)) {
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_MM;
+		mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0);
+	} else {
+		priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR);
+		mc_filter[1] = mc_filter[0] = 0;
+		mclist = NULL;
+		while ((mclist = ieee80211_get_mc_list_item(dev, mclist, &tmp))) {
+			bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+			bit_nr &= 0x3F;
+			mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31));
+		}
+	}
+
+	ADM8211_IDLE_RX();
+
+	ADM8211_CSR_WRITE(MAR0, mc_filter[0]);
+	ADM8211_CSR_WRITE(MAR1, mc_filter[1]);
+	ADM8211_CSR_READ(NAR);
+
+	ADM8211_RESTORE();
+}
+
+static int adm8211_get_tx_stats(struct net_device *dev,
+				struct ieee80211_tx_queue_stats *stats)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	struct ieee80211_tx_queue_stats_data *data = &stats->data[0];
+
+	data->len = priv->cur_tx - priv->dirty_tx;
+	data->limit = priv->tx_ring_size - 2;
+	data->count = priv->dirty_tx;
+
+	return 0;
+}
+
+static void adm8211_interrupt_tci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	struct net_device_stats *stats = ieee80211_dev_stats(dev);
+	unsigned dirty_tx;
+
+	spin_lock(&priv->lock);
+
+	for (dirty_tx = priv->dirty_tx;
+	     priv->cur_tx - dirty_tx > 0; dirty_tx++) {
+		unsigned entry = dirty_tx % priv->tx_ring_size;
+		u32 status = le32_to_cpu(priv->tx_ring[entry].status);
+		struct sk_buff *skb;
+
+		if (status & TDES0_CONTROL_OWN ||
+		    !(status & TDES0_CONTROL_DONE))
+			break;
+
+		if (status & TDES0_STATUS_ES) {
+			stats->tx_errors++;
+			priv->tx_buffers[entry].tx_status.ack = 0;
+
+			if (status & (TDES0_STATUS_TUF | TDES0_STATUS_TRO))
+				stats->tx_fifo_errors++;
+		} else
+			priv->tx_buffers[entry].tx_status.ack = 1;
+
+		skb = priv->tx_buffers[entry].skb;
+		pci_unmap_single(priv->pdev, priv->tx_buffers[entry].mapping,
+				 skb->len, PCI_DMA_TODEVICE);
+
+		if (priv->tx_buffers[entry].tx_status.control.req_tx_status ||
+		    priv->tx_buffers[entry].unicast)
+			ieee80211_tx_status_irqsafe(dev, priv->tx_buffers[entry].skb,
+						    &priv->tx_buffers[entry].tx_status);
+		else
+			dev_kfree_skb_irq(priv->tx_buffers[entry].skb);
+		priv->tx_buffers[entry].skb = NULL;
+	}
+
+	if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2)
+		netif_wake_queue(dev);
+
+	priv->dirty_tx = dirty_tx;
+	spin_unlock(&priv->lock);
+}
+
+
+static void adm8211_interrupt_rci(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	struct net_device_stats *stats = ieee80211_dev_stats(dev);
+	unsigned int entry = priv->cur_rx % priv->rx_ring_size;
+	u32 status;
+	unsigned pktlen;
+	struct sk_buff *skb, *newskb;
+	unsigned int limit = priv->rx_ring_size;
+	static const u8 rate_tbl[] = {10, 20, 55, 110, 220};
+	u8 rssi, rate;
+
+	while (!(priv->rx_ring[entry].status &
+		 __constant_cpu_to_le32(RDES0_STATUS_OWN))) {
+		if (limit-- == 0)
+			break;
+
+		status = le32_to_cpu(priv->rx_ring[entry].status);
+		rate = (status & RDES0_STATUS_RXDR) >> 12;
+		rssi = le32_to_cpu(priv->rx_ring[entry].length) &
+			RDES1_STATUS_RSSI;
+
+		pktlen = status & RDES0_STATUS_FL;
+		if (pktlen > RX_PKT_SIZE) {
+			if (net_ratelimit())
+				printk(KERN_DEBUG "%s: too long frame (pktlen=%d)\n",
+				       dev->name, pktlen);
+			pktlen = RX_PKT_SIZE;
+		}
+
+		if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) {
+			skb = NULL; /* old buffer will be reused */
+			stats->rx_errors++;
+			if (status & (RDES0_STATUS_CRC16E | RDES0_STATUS_CRC32E))
+				stats->rx_crc_errors++;
+
+		} else if (pktlen < RX_COPY_BREAK) {
+			skb = dev_alloc_skb(pktlen);
+			if (skb) {
+				skb->dev = dev;
+				pci_dma_sync_single_for_cpu(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					pktlen, PCI_DMA_FROMDEVICE);
+				memcpy(skb_put(skb, pktlen),
+				       priv->rx_buffers[entry].skb->tail,
+				       pktlen);
+				pci_dma_sync_single_for_device(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+			}
+		} else {
+			newskb = dev_alloc_skb(RX_PKT_SIZE);
+			if (newskb) {
+				newskb->dev = dev;
+				skb = priv->rx_buffers[entry].skb;
+				skb_put(skb, pktlen);
+				pci_unmap_single(
+					priv->pdev,
+					priv->rx_buffers[entry].mapping,
+					RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+				priv->rx_buffers[entry].skb = newskb;
+				priv->rx_buffers[entry].mapping =
+					pci_map_single(priv->pdev,
+						       newskb->tail,
+						       RX_PKT_SIZE,
+						       PCI_DMA_FROMDEVICE);
+			} else {
+				skb = NULL;
+				stats->rx_dropped++;
+			}
+
+			priv->rx_ring[entry].buffer1 =
+				cpu_to_le32(priv->rx_buffers[entry].mapping);
+		}
+
+		priv->rx_ring[entry].status = cpu_to_le32( RDES0_STATUS_OWN | 
RDES0_STATUS_SQL );
+		priv->rx_ring[entry].length =
+			cpu_to_le32(RX_PKT_SIZE |
+				    (entry == priv->rx_ring_size - 1 ?
+				     RDES1_CONTROL_RER : 0));
+
+		if (skb) {
+			struct ieee80211_rx_status rx_status = {0};
+
+			if (priv->revid < ADM8211_REV_CA)
+				rx_status.ssi = rssi;
+			else
+				rx_status.ssi = 100 - rssi;
+
+			if (rate <= 4)
+				rx_status.rate = rate_tbl[rate];
+	
+			/* remove FCS */
+			/* TODO: remove this and set flag in ieee80211_hw instead? */
+			if (dev->flags & IFF_PROMISC)
+				skb_trim(skb, skb->len - FCS_LEN);
+
+			ieee80211_rx_irqsafe(dev, skb, &rx_status);
+		}
+
+		entry = (++priv->cur_rx) % priv->rx_ring_size;
+	}
+
+	stats->rx_missed_errors += le32_to_cpu(ADM8211_CSR_READ(LPC)) & 0xFFFF;
+}
+
+
+static irqreturn_t adm8211_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+#define ADM8211_INT(x) if (unlikely(stsr & ADM8211_STSR_ ## x)) 
printk(KERN_DEBUG "%s: " #x "\n", dev->name)
+
+	struct net_device *dev = dev_id;
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int count = 0;
+	u32 stsr;
+
+	do {
+		stsr = le32_to_cpu(ADM8211_CSR_READ(STSR));
+		ADM8211_CSR_WRITE(STSR, cpu_to_le32(stsr));
+		if (stsr == 0xffffffff)
+			return IRQ_HANDLED;
+
+		if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS)))
+			break;
+
+		if (stsr & ADM8211_STSR_RCI)
+			adm8211_interrupt_rci(dev);
+		if (stsr & ADM8211_STSR_TCI)
+			adm8211_interrupt_tci(dev);
+
+		if ((stsr & (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff))
+			 != (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) {
+			if (stsr & ADM8211_STSR_LinkOn)
+				printk(KERN_DEBUG "%s: LinkOn\n", dev->name);
+		
+			if (stsr & ADM8211_STSR_LinkOff)
+				printk(KERN_DEBUG "%s: LinkOff\n", dev->name);
+		}
+
+		ADM8211_INT(PCF);
+		ADM8211_INT(BCNTC);
+		ADM8211_INT(GPINT);
+		ADM8211_INT(ATIMTC);
+		ADM8211_INT(TSFTF);
+		ADM8211_INT(TSCZ);
+		ADM8211_INT(SQL);
+		ADM8211_INT(WEPTD);
+		ADM8211_INT(ATIME);
+		/*ADM8211_INT(TBTT);*/
+		ADM8211_INT(TEIS);
+		ADM8211_INT(FBE);
+		ADM8211_INT(REIS);
+		ADM8211_INT(GPTT);
+		ADM8211_INT(RPS);
+		ADM8211_INT(RDU);
+		ADM8211_INT(TUF);
+		/*ADM8211_INT(TRT);*/
+		/*ADM8211_INT(TLT);*/
+		/*ADM8211_INT(TDU);*/
+		ADM8211_INT(TPS);
+
+	} while (count++ < 20);
+
+	return IRQ_RETVAL(count);
+
+#undef ADM8211_INT
+}
+
+#define 
WRITE_SYN(valmask,valshift,addrmask,addrshift,bits,prewrite,postwrite) do {\
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);\
+	unsigned int i;\
+	u32 reg, bitbuf;\
+	\
+	value &= valmask;\
+	addr &= addrmask;\
+	bitbuf = (value << valshift) | (addr << addrshift);\
+	\
+	ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_1));
\
+	ADM8211_CSR_READ(SYNRF);\
+	ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_0));
\
+	ADM8211_CSR_READ(SYNRF);\
+	\
+	if (prewrite) {\
+		ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32
(ADM8211_SYNRF_WRITE_SYNDATA_0));\
+		ADM8211_CSR_READ(SYNRF);\
+	}\
+	\
+	for (i = 0; i <= bits; i++) {\
+		if ( bitbuf & (1 << (bits - i)) )\
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_1;\
+		else\
+			reg = ADM8211_SYNRF_WRITE_SYNDATA_0;\
+		\
+		ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg));\
+		ADM8211_CSR_READ(SYNRF);\
+		\
+		ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_1));\
+		ADM8211_CSR_READ(SYNRF);\
+		ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_0));\
+		ADM8211_CSR_READ(SYNRF);\
+	}\
+	\
+	if (postwrite == 1) {\
+		ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_0));\
+		ADM8211_CSR_READ(SYNRF);\
+	}\
+	if (postwrite == 2) {\
+		ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_1));\
+		ADM8211_CSR_READ(SYNRF);\
+	}\
+	\
+	ADM8211_CSR_WRITE(SYNRF, 0);\
+	ADM8211_CSR_READ(SYNRF);\
+} while (0)
+
+static void adm8211_rf_write_syn_max2820 (struct net_device *dev, u16 addr, 
u32 value)
+{
+	WRITE_SYN(0x00FFF, 0, 0x0F, 12, 15, 1, 1);
+}
+
+static void adm8211_rf_write_syn_al2210l (struct net_device *dev, u16 addr, 
u32 value)
+{
+	WRITE_SYN(0xFFFFF, 4, 0x0F,  0, 23, 1, 1);
+}
+
+static void adm8211_rf_write_syn_rfmd2958 (struct net_device *dev, u16 addr, 
u32 value)
+{
+	WRITE_SYN(0x3FFFF, 0, 0x1F, 18, 23, 0, 1);
+}
+
+static void adm8211_rf_write_syn_rfmd2948 (struct net_device *dev, u16 addr, 
u32 value)
+{
+	WRITE_SYN(0x0FFFF, 4, 0x0F,  0, 21, 0, 2);
+}
+
+static int adm8211_write_bbp(struct net_device *dev, u8 addr, u8 data)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int timeout;
+	u32 reg;
+
+	timeout = 10;
+	while (timeout > 0) {
+		reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
+		if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD)))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed prewrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	switch (priv->bbp_type) {
+	case ADM8211_TYPE_INTERSIL:
+		reg = ADM8211_BBPCTL_MMISEL;	/* three wire interface */
+		break;
+	case ADM8211_TYPE_RFMD:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x01<<18);
+		break;
+	case ADM8211_TYPE_ADMTEK:
+		reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP |
+		      (0x05<<18);
+		break;
+	}
+	reg |= ADM8211_BBPCTL_WR | (addr << 8) | data;
+
+	ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg));
+
+	timeout = 10;
+	while (timeout > 0) {
+		reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
+		if (!(reg & ADM8211_BBPCTL_WR))
+			break;
+		timeout--;
+		mdelay(2);
+	}
+
+	if (timeout == 0) {
+		ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) &
+				  __constant_cpu_to_le32(~ADM8211_BBPCTL_WR));
+		printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed postwrite "
+		       "(reg=0x%08x)\n",
+		       dev->name, addr, data, reg);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int adm8211_rf_set_channel(struct net_device *dev, unsigned int 
channel)
+{
+	static const u32 adm8211_rfmd2958_reg5[] =
+		{0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340,
+		 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7};
+	static const u32 adm8211_rfmd2958_reg6[] =
+		{0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000,
+		 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745};
+
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u8 ant_power = priv->ant_power > 0x3F ?
+		priv->eeprom->antenna_power[channel-1] : priv->ant_power;
+	u8 tx_power = priv->tx_power > 0x3F ?
+		priv->eeprom->tx_power[channel-1] : priv->tx_power;
+	u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ?
+		priv->eeprom->lpf_cutoff[channel-1] : priv->lpf_cutoff;
+	u8 lnags_thresh = priv->lnags_threshold == 0xFF ?
+		priv->eeprom->lnags_threshold[channel-1] : priv->lnags_threshold;
+	u32 reg;
+
+	if (channel < 1 || channel > 14)
+		return -EINVAL;
+
+	ADM8211_IDLE();
+
+	/* Program synthesizer to new channel */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033);
+
+		adm8211_rf_write_syn_rfmd2958(dev, 0x05,
+					adm8211_rfmd2958_reg5[channel-1]);
+		adm8211_rf_write_syn_rfmd2958(dev, 0x06,
+					adm8211_rfmd2958_reg6[channel-1]);
+		break;
+
+	case ADM8211_RFMD2948:
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN,
+				     SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV,
+				     (channel == 14 ? 2110 : (2033 + (channel * 5))));
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44);
+		adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x3,
+			(channel == 14 ? 0x054 : (0x7 + (channel * 5))));
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0,
+			(channel == 14 ? 0x229B4 : (0x22967 + (channel * 5))));
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+
+	/* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */
+	/* TODO: remove if SMC 2635W doesn't need this */
+	if (priv->transceiver_type == ADM8211_RFMD2948) {
+		reg = le32_to_cpu(ADM8211_CSR_READ(GPIO));
+		reg &= 0xfffc0000;
+		reg |= ADM8211_CSR_GPIO_EN0;
+		if (channel != 14)
+			reg |= ADM8211_CSR_GPIO_O0;
+		ADM8211_CSR_WRITE(GPIO, cpu_to_le32(reg));
+	}
+
+	if (priv->transceiver_type == ADM8211_RFMD2958) {
+		/* set PCNT2 */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100);
+		/* set PCNT1 P_DESIRED/MID_BIAS */
+		reg = le16_to_cpu(priv->eeprom->cr49);
+		reg >>= 13;
+		reg <<= 15;
+		reg |= ant_power<<9;
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg);
+		/* set TXRX TX_GAIN */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 |
+			(priv->revid < ADM8211_REV_CA ? tx_power : 0));
+	} else {
+		reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD));
+		reg &= 0xff00ffff;
+		reg |= tx_power<<18;
+		ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg));
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF |
+			  ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST));
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(30);
+
+	/* RF3000 BBP */
+	if (priv->transceiver_type != ADM8211_RFMD2958)
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT,
+				  tx_power<<2);
+	adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff);
+	adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh);
+	adm8211_write_bbp(dev, 0x1c, priv->revid == ADM8211_REV_BA
+			  ? priv->eeprom->cr28 : 0);
+	adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	} else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) {	/* Nothing to do for 
ADMtek BBP */
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_RESTORE();
+
+	/* update current channel for adhoc (and maybe AP mode) */
+	reg = le32_to_cpu(ADM8211_CSR_READ(CAP0));
+	reg &= ~0xF;
+	reg |= channel;
+	ADM8211_CSR_WRITE(CAP0, cpu_to_le32(reg));
+
+	return 0;
+}
+
+static void adm8211_update_mode(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	ADM8211_IDLE();
+
+	priv->soft_rx_crc = 0;
+	switch (priv->mode) {
+	case IEEE80211_IF_TYPE_STA:
+		priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA);
+		priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR;
+		break;
+	case IEEE80211_IF_TYPE_IBSS:
+		priv->nar &= ~ADM8211_NAR_PR;
+		priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR;
+
+		/* don't trust the error bits on rev 0x20 and up in adhoc */
+		if (priv->revid >= ADM8211_REV_BA)
+			priv->soft_rx_crc = 1;
+		break;
+	case IEEE80211_IF_TYPE_MNTR:
+		priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST);
+		priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR;
+		break;
+	}
+
+	ADM8211_RESTORE();
+}
+
+static void adm8211_hw_init_syn(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		/* comments taken from ADMtek driver */
+		
+		/* Reset RF2958 after power on */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000);
+		/* Initialize RF VCO Core Bias to maximum */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F);
+		/* Initialize IF PLL */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03);
+		/* Initialize IF PLL Coarse Tuning */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F);
+		/* Initialize RF PLL */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403);
+		/* Initialize RF PLL Coarse Tuning */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F);
+		/* Initialize TX gain and filter BW (R9) */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x09,
+			(priv->transceiver_type == ADM8211_RFMD2958
+			? 0x10050 : 0x00050) );
+		/* Initialize CAL register */
+		adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E);
+		adm8211_rf_write_syn_max2820(dev, 0x2, 0x001);
+		adm8211_rf_write_syn_max2820(dev, 0x3, 0x054);
+		adm8211_rf_write_syn_max2820(dev, 0x4, 0x310);
+		adm8211_rf_write_syn_max2820(dev, 0x5, 0x000);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C);
+		adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB);
+		adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F);
+		adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9);
+		adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280);
+		adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641);
+		adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130);
+		adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000);
+		adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F);
+		adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C);
+		adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000);
+		adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000);
+		break;
+
+	case ADM8211_RFMD2948:
+	default:
+		break;
+	}
+}
+
+static int adm8211_hw_init_bbp(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+
+	/* write addresses */
+	if (priv->bbp_type == ADM8211_TYPE_INTERSIL) {
+		ADM8211_CSR_WRITE(MMIWA,  __constant_cpu_to_le32(0x100E0C0A));
+		ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00007c7e));
+		ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x00100000));
+	} else if (priv->bbp_type == ADM8211_TYPE_RFMD ||
+		   priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+
+	/* check specific BBP type */
+	switch (priv->specific_bbptype) {
+	case ADM8211_BBP_RFMD3000:
+	case ADM8211_BBP_RFMD3002:
+		ADM8211_CSR_WRITE(MMIWA,  __constant_cpu_to_le32(0x00009101));
+		ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00000301));
+		break;
+
+	case ADM8211_BBP_ADM8011:
+		ADM8211_CSR_WRITE(MMIWA,  __constant_cpu_to_le32(0x00008903));
+		ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00001716));
+
+		reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL));
+		reg &= ~ADM8211_BBPCTL_TYPE;
+		reg |= 0x5 << 18;
+		ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg));
+		break;
+	}
+
+	switch (priv->revid) {
+	case ADM8211_REV_CA:
+		if (priv->transceiver_type == ADM8211_RFMD2958 ||
+		    priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER ||
+		    priv->transceiver_type == ADM8211_RFMD2948)
+			ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x1 << 22));
+		else if (priv->transceiver_type == ADM8211_MAX2820 ||
+			 priv->transceiver_type == ADM8211_AL2210L)
+			ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x3 << 22));
+		break;
+
+	case ADM8211_REV_BA:
+		reg  = le32_to_cpu(ADM8211_CSR_READ(MMIRD1));
+		reg &= 0x0000FFFF;
+		reg |= 0x7e100000;
+		ADM8211_CSR_WRITE(MMIRD1, cpu_to_le32(reg));
+		break;
+
+	case ADM8211_REV_AB:
+	case ADM8211_REV_AF:
+	default:
+		ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x7e100000));
+		break;
+	}
+
+	/* For RFMD */
+	ADM8211_CSR_WRITE(MACTEST, __constant_cpu_to_le32(0x800));
+	}
+
+	adm8211_hw_init_syn(dev);
+
+	/* Set RF Power control IF pin to PE1+PHYRST# */
+	ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF |
+			  ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST));
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(20);
+
+	/* write BBP regs */
+	if (priv->bbp_type == ADM8211_TYPE_RFMD) {
+		/* RF3000 BBP */
+		/* another set:
+		 * 11: c8
+		 * 14: 14
+		 * 15: 50 (chan 1..13; chan 14: d0)
+		 * 1c: 00
+		 * 1d: 84
+		 */
+		adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80);
+		adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); /* antenna selection: 
diversity */
+		adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74);
+		adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38);
+		adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40);
+
+		if (priv->eeprom->major_version < 2) {
+			adm8211_write_bbp(dev, 0x1c, 0x00);
+			adm8211_write_bbp(dev, 0x1d, 0x80);
+		} else {
+			if (priv->revid == ADM8211_REV_BA)
+				adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28);
+			else
+				adm8211_write_bbp(dev, 0x1c, 0x00);
+
+			adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29);
+		}
+	} else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) {
+	adm8211_write_bbp(dev, 0x00, 0xFF);	/* reset baseband */
+	adm8211_write_bbp(dev, 0x07, 0x0A);	/* antenna selection: diversity */
+
+	/* TODO: find documentation for this */
+	switch (priv->transceiver_type) {
+	case ADM8211_RFMD2958:
+	case ADM8211_RFMD2958_RF3000_CONTROL_POWER:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x00);
+		adm8211_write_bbp(dev, 0x0f, 0xAA);
+		adm8211_write_bbp(dev, 0x10, 0x8c);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x40);
+		adm8211_write_bbp(dev, 0x20, 0x23);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x28);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x28, 0x35);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x2d, 0x0A);
+		adm8211_write_bbp(dev, 0x29, 0x40);
+		adm8211_write_bbp(dev, 0x60, 0x08);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_MAX2820:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x09, 0x05);
+		adm8211_write_bbp(dev, 0x0a, 0x02);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x0f);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0x8c);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0x4a);
+		adm8211_write_bbp(dev, 0x60, 0x2b);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_AL2210L:
+		adm8211_write_bbp(dev, 0x00, 0x00);
+		adm8211_write_bbp(dev, 0x01, 0x00);
+		adm8211_write_bbp(dev, 0x02, 0x00);
+		adm8211_write_bbp(dev, 0x03, 0x00);
+		adm8211_write_bbp(dev, 0x06, 0x0f);
+		adm8211_write_bbp(dev, 0x07, 0x05);
+		adm8211_write_bbp(dev, 0x08, 0x03);
+		adm8211_write_bbp(dev, 0x09, 0x00);
+		adm8211_write_bbp(dev, 0x0a, 0x00);
+		adm8211_write_bbp(dev, 0x0b, 0x00);
+		adm8211_write_bbp(dev, 0x0c, 0x10);
+		adm8211_write_bbp(dev, 0x0f, 0x55);
+		adm8211_write_bbp(dev, 0x10, 0x8d);
+		adm8211_write_bbp(dev, 0x11, 0x43);
+		adm8211_write_bbp(dev, 0x18, 0x4a);
+		adm8211_write_bbp(dev, 0x20, 0x20);
+		adm8211_write_bbp(dev, 0x21, 0x02);
+		adm8211_write_bbp(dev, 0x22, 0x23);
+		adm8211_write_bbp(dev, 0x23, 0x30);
+		adm8211_write_bbp(dev, 0x24, 0x2d);
+		adm8211_write_bbp(dev, 0x2a, 0xaa);
+		adm8211_write_bbp(dev, 0x2b, 0x81);
+		adm8211_write_bbp(dev, 0x2c, 0x44);
+		adm8211_write_bbp(dev, 0x29, 0xfa);
+		adm8211_write_bbp(dev, 0x60, 0x2d);
+		adm8211_write_bbp(dev, 0x64, 0x01);
+		break;
+
+	case ADM8211_RFMD2948:
+		break;
+
+	default:
+		printk(KERN_DEBUG "%s: unsupported transceiver type %d\n",
+		       dev->name, priv->transceiver_type);
+		break;
+	}
+	} else {
+		printk(KERN_DEBUG "%s: unsupported BBP type %d\n",
+		       dev->name, priv->bbp_type);
+	}
+
+	ADM8211_CSR_WRITE(SYNRF, 0);
+
+	/* Set RF CAL control source to MAC control */
+	reg = le32_to_cpu(ADM8211_CSR_READ(SYNCTL));
+	reg |= ADM8211_SYNCTL_SELCAL;
+	ADM8211_CSR_WRITE(SYNCTL, cpu_to_le32(reg));
+
+	return 0;
+}
+
+// configures hw beacons/probe responses
+static int adm8211_set_rate(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+	int i = 0;
+	u8 rate_buf[12] = {0};
+
+	/* write supported rates */
+	if (priv->revid != ADM8211_REV_BA) {
+		rate_buf[0] = ARRAY_SIZE(adm8211_rates);
+		for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++)
+			rate_buf[i+1] = (adm8211_rates[i].rate/5) | 0x80;
+	} else {
+		/* workaround for rev BA specific bug */
+		rate_buf[0]=4;
+		rate_buf[1]=0x82;
+		rate_buf[2]=0x04;
+		rate_buf[3]=0x0b;
+		rate_buf[4]=0x16;
+	}
+	
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, 
ARRAY_SIZE(adm8211_rates)+1);
+
+	reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD)) & 0x00FFFFFF; /* keep bits 0-23 
*/
+	reg |= (1 << 15);	/* short preamble */
+	reg |= 110 << 24;
+	ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg));
+
+	/* MTMLT   = 512 TU (max TX MSDU lifetime)
+	 * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate)
+	 * SRTYLIM = 224 (short retry limit, value in TX header used by default) */
+	ADM8211_CSR_WRITE(TXLMT, cpu_to_le32((512<<16) | (110<<8) | (224<<0)));
+
+	return 0;
+}
+
+static void adm8211_hw_init(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+	u8 cacheline;
+
+	reg = le32_to_cpu(ADM8211_CSR_READ(PAR));
+	reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME;
+	reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL);
+
+	if (!pci_set_mwi(priv->pdev)) {
+		reg |= (0x1<<24);
+		pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cacheline);
+
+		switch (cacheline) {
+		case  0x8: reg |= (0x1<<14);
+			   break;
+		case 0x16: reg |= (0x2<<14);
+			   break;
+		case 0x32: reg |= (0x3<<14);
+			   break;
+		  default: reg |= (0x0<<14);
+			   break;
+		}
+	}
+
+	ADM8211_CSR_WRITE(PAR, cpu_to_le32(reg));
+
+	reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
+	reg &= ~(0xF<<28);
+	reg |= ((1 << 28) | (1 << 31));
+	ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
+
+	/* lose link after 4 lost beacons */
+	reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE;
+	ADM8211_CSR_WRITE(WCSR, cpu_to_le32(reg));
+
+	/* Disable APM, enable receive FIFO threshold, and set drain receive
+	 * threshold to store-and-forward */
+	reg = le32_to_cpu(ADM8211_CSR_READ(CMDR));
+	reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT);
+	reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF;
+	ADM8211_CSR_WRITE(CMDR, cpu_to_le32(reg));
+
+	adm8211_set_rate(dev);
+
+	/* 4-bit values:
+	 * PWR1UP   = 8 * 2 ms
+	 * PWR0PAPE = 8 us or 5 us
+	 * PWR1PAPE = 1 us or 3 us
+	 * PWR0TRSW = 5 us
+	 * PWR1TRSW = 12 us
+	 * PWR0PE2  = 13 us
+	 * PWR1PE2  = 1 us
+	 * PWR0TXPE = 8 or 6 */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8815cd18));
+	else
+		ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8535cd16));
+
+	/* Enable store and forward for transmit */
+	priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB;
+	ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar));
+
+	/* Reset RF */
+	ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_RADIO));
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(10);
+	ADM8211_CSR_WRITE(SYNRF, 0);
+	ADM8211_CSR_READ(SYNRF);
+	mdelay(5);
+
+	/* Set CFP Max Duration to 0x10 TU */
+	reg = le32_to_cpu(ADM8211_CSR_READ(CFPP));
+	reg &= ~(0xffff<<8);
+	reg |= 0x0010<<8;
+	ADM8211_CSR_WRITE(CFPP, cpu_to_le32(reg));
+
+	/* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us
+	 * TUCNT = 0x3ff - Tu counter 1024 us  */
+	ADM8211_CSR_WRITE(TOFS0, __constant_cpu_to_le32((0x16 << 24) | 0x3ff));
+
+	/* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us),
+	 * DIFS=50 us, EIFS=100 us */
+	if (priv->revid < ADM8211_REV_CA)
+		ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32(
+					(20 << 23) | (110 << 15) |
+					(50 << 9)  | 100));
+	else
+		ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32(
+					(20 << 23) | (24 << 15) |
+					(50 << 9)  | 100));
+
+	/* PCNT = 1 (MAC idle time awake/sleep, unit S)
+	 * RMRD = 2346 * 8 + 1 us (max RX duration)  */
+	ADM8211_CSR_WRITE(RMD, __constant_cpu_to_le32((1 << 16) | 18769));
+
+	/* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */
+	ADM8211_CSR_WRITE(RSPT, __constant_cpu_to_le32(0xffffff00));
+
+	/* Initialize BBP (and SYN) */
+	adm8211_hw_init_bbp(dev);
+
+	/* make sure interrupts are off */
+	ADM8211_CSR_WRITE(IER, 0);
+
+	/* ACK interrupts */
+	ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR));
+
+	/* Setup WEP (turns it off for now) */
+	reg = le32_to_cpu(ADM8211_CSR_READ(MACTEST));
+	reg &= ~(7<<20);
+	ADM8211_CSR_WRITE(MACTEST, cpu_to_le32(reg));
+
+	reg = le32_to_cpu(ADM8211_CSR_READ(WEPCTL));
+	reg &= ~ADM8211_WEPCTL_WEPENABLE;
+	reg |= ADM8211_WEPCTL_WEPRXBYP;
+	ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(reg));
+
+	/* Clear the missed-packet counter. */
+	ADM8211_CSR_READ(LPC);
+
+	/* set mac address */
+	ADM8211_CSR_WRITE(PAR0, *(u32 *)dev->dev_addr);
+	ADM8211_CSR_WRITE(PAR1, *(u16 *)(dev->dev_addr + 4));
+}
+
+static int adm8211_hw_reset(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+	__le32 tmp;
+	int timeout = 50;
+
+	/* Power-on issue */
+	/* TODO: check if this is necessary */
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	/* Reset the chip */
+	tmp = ADM8211_CSR_READ(PAR);
+	ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR);
+
+	while ((ADM8211_CSR_READ(PAR) & __constant_cpu_to_le32(ADM8211_PAR_SWR)) && 
timeout--)
+		mdelay(100);
+
+	if (timeout <= 0)
+		return -ETIMEDOUT;
+
+	ADM8211_CSR_WRITE(PAR, tmp);
+
+	if (priv->revid == ADM8211_REV_BA &&
+	    ( priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER
+	   || priv->transceiver_type == ADM8211_RFMD2958)) {
+		reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
+		reg |= (1 << 4) | (1 << 5);
+		ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
+	} else if (priv->revid == ADM8211_REV_CA) {
+		reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1));
+		reg &= ~((1 << 4) | (1 << 5));
+		ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg));
+	}
+
+	ADM8211_CSR_WRITE(FRCTL, 0);
+
+	reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST0));
+	reg |= ADM8211_CSR_TEST0_EPRLD;	/* EEPROM Recall */
+	ADM8211_CSR_WRITE(CSR_TEST0, cpu_to_le32(reg));
+
+	adm8211_clear_sram(dev);
+
+	return 0;
+}
+
+static u64 adm8211_get_tsft(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 tsftl;
+	u64 tsft;
+
+	tsftl = le32_to_cpu(ADM8211_CSR_READ(TSFTL));
+	tsft = le32_to_cpu(ADM8211_CSR_READ(TSFTH));
+	tsft <<= 32;
+	tsft |= tsftl;
+
+	return tsft;
+}
+
+static void adm8211_set_interval(struct net_device *dev,
+				 unsigned short bi, unsigned short li)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+
+	/* BP (beacon interval) = data->beacon_interval
+	 * LI (listen interval) = data->listen_interval (in beacon intervals) */
+	reg = (bi << 16) | li;
+	ADM8211_CSR_WRITE(BPLI, cpu_to_le32(reg));
+}
+
+static void adm8211_set_bssid(struct net_device *dev, u8 *bssid)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u32 reg;
+
+	reg = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24);
+	ADM8211_CSR_WRITE(BSSID0, cpu_to_le32(reg));
+	reg = le32_to_cpu(ADM8211_CSR_READ(ABDA1));
+	reg &= 0x0000ffff;
+	reg |= (bssid[4] << 16) | (bssid[5] << 24);
+	ADM8211_CSR_WRITE(ABDA1, cpu_to_le32(reg));
+}
+
+static int adm8211_set_ssid(struct net_device *dev, u8 *ssid, size_t 
ssid_len)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	u8 buf[36];
+
+	if (ssid_len > 32)
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	buf[0] = ssid_len;
+	memcpy(buf + 1, ssid, ssid_len);
+	adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33);
+	//adm8211_set_beacon(dev);
+	return 0;
+}
+
+static int adm8211_config(struct net_device *dev, struct ieee80211_conf 
*conf)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	if (conf->channel != priv->channel) {
+		priv->channel = conf->channel;
+		adm8211_rf_set_channel(dev, priv->channel);
+	}
+
+	return 0;
+}
+
+static int adm8211_config_interface(struct net_device *dev, int if_id,
+				    struct ieee80211_if_conf *conf)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) {
+		adm8211_set_bssid(dev, conf->bssid);
+		memcpy(priv->bssid, conf->bssid, ETH_ALEN);
+	}
+
+	if (conf->ssid_len != priv->ssid_len ||
+	    memcmp(conf->ssid, priv->ssid, conf->ssid_len)) {
+		adm8211_set_ssid(dev, conf->ssid, conf->ssid_len);
+		priv->ssid_len = conf->ssid_len;
+		memcpy(priv->ssid, conf->ssid, conf->ssid_len);
+	}
+
+	return 0;
+}
+static int adm8211_scan(struct net_device *dev, int state,
+			struct ieee80211_scan_conf *conf)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	if (state == IEEE80211_SCAN_START) {
+		priv->channel = conf->scan_channel;
+		adm8211_rf_set_channel(dev, conf->scan_channel);
+	} else if (state == IEEE80211_SCAN_END) {
+		priv->channel = conf->running_channel;
+		adm8211_rf_set_channel(dev, conf->running_channel);
+	}
+
+	return 0;
+}
+
+static int adm8211_add_interface(struct net_device *dev,
+				 struct ieee80211_if_init_conf *conf)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	/* NOTE: using IEEE80211_IF_TYPE_MGMT to indicate no mode selected */
+	if (priv->mode != IEEE80211_IF_TYPE_MGMT)
+		return -1;
+
+	switch (conf->type) {
+	case IEEE80211_IF_TYPE_STA:
+	case IEEE80211_IF_TYPE_MNTR:
+		priv->mode = conf->type;
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+static void adm8211_remove_interface(struct net_device *dev,
+				     struct ieee80211_if_init_conf *conf)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	priv->mode = IEEE80211_IF_TYPE_MGMT;
+}
+
+static int adm8211_init_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	struct adm8211_desc *desc = NULL;
+	struct adm8211_ring_info *info;
+	unsigned int i;
+
+	for (i = 0; i < priv->rx_ring_size; i++) {
+		desc = &priv->rx_ring[i];
+		desc->status = 0;
+		desc->length = cpu_to_le32(RX_PKT_SIZE);
+		priv->rx_buffers[i].skb = NULL;
+	}
+	/* Mark the end of RX ring; hw returns to base address after this
+	 * descriptor */
+	desc->length |= cpu_to_le32(RDES1_CONTROL_RER);
+
+	for (i = 0; i < priv->rx_ring_size; i++) {
+		desc = &priv->rx_ring[i];
+		info = &priv->rx_buffers[i];
+
+		info->skb = dev_alloc_skb(RX_PKT_SIZE);
+		if (info->skb == NULL)
+			break;
+		info->mapping = pci_map_single(priv->pdev, info->skb->tail,
+					       RX_PKT_SIZE,
+					       PCI_DMA_FROMDEVICE);
+		info->skb->dev = dev;
+		desc->buffer1 = cpu_to_le32(info->mapping);
+		desc->status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL );
+	}
+
+	/* Setup TX ring. TX buffers descriptors will be filled in as needed */
+	for (i = 0; i < priv->tx_ring_size; i++) {
+		desc = &priv->tx_ring[i];
+		info = &priv->tx_buffers[i];
+
+		info->skb = NULL;
+		info->mapping = 0;
+		desc->status = 0;
+	}
+	desc->length = cpu_to_le32(TDES1_CONTROL_TER);
+
+	priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0;
+	ADM8211_CSR_WRITE(RDB, cpu_to_le32(priv->rx_ring_dma));
+	ADM8211_CSR_WRITE(TDBD, cpu_to_le32(priv->tx_ring_dma));
+
+	return 0;
+}
+
+static void adm8211_free_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int i;
+
+	for (i = 0; i < priv->rx_ring_size; i++) {
+		if (!priv->rx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->rx_buffers[i].mapping,
+			RX_PKT_SIZE, PCI_DMA_FROMDEVICE);
+
+		dev_kfree_skb(priv->rx_buffers[i].skb);
+	}
+
+	for (i = 0; i < priv->tx_ring_size; i++) {
+		if (!priv->tx_buffers[i].skb)
+			continue;
+
+		pci_unmap_single(
+			priv->pdev,
+			priv->tx_buffers[i].mapping,
+			priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE);
+
+		dev_kfree_skb(priv->tx_buffers[i].skb);
+	}
+}
+
+static int adm8211_open(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	int retval;
+
+	/* Power up MAC and RF chips */
+	retval = adm8211_hw_reset(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: hardware reset failed\n", dev->name);
+		goto fail;
+	}
+
+	retval = adm8211_init_rings(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: failed to initialize rings\n", dev->name);
+		goto fail;
+	}
+
+	/* Init hardware */
+	adm8211_hw_init(dev);
+	adm8211_rf_set_channel(dev, priv->channel);
+
+	retval = request_irq(dev->irq, &adm8211_interrupt,
+			     SA_SHIRQ, dev->name, dev);
+	if (retval) {
+		printk(KERN_ERR "%s: failed to register IRQ handler\n",
+		       dev->name);
+		goto fail;
+	}
+
+	ADM8211_CSR_WRITE(IER, __constant_cpu_to_le32(ADM8211_INTMASK));
+	adm8211_update_mode(dev);
+	ADM8211_CSR_WRITE(RDR, 0);
+
+	adm8211_set_interval(dev, 100, 10);
+	return 0;
+
+fail:
+	return retval;
+}
+
+static int adm8211_stop(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+
+	priv->nar = 0;
+	ADM8211_CSR_WRITE(NAR, 0);
+	ADM8211_CSR_WRITE(IER, 0);
+	ADM8211_CSR_READ(NAR);
+
+	free_irq(dev->irq, dev);
+
+	adm8211_free_rings(dev);
+
+	adm8211_hw_reset(dev);
+	return 0;
+}
+
+static int adm8211_reset(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	int retval = 0;
+
+	priv->nar = 0;
+	ADM8211_CSR_WRITE(NAR, 0);
+	ADM8211_CSR_WRITE(IER, 0);
+	ADM8211_CSR_READ(NAR);
+
+	adm8211_free_rings(dev);
+
+	retval = adm8211_hw_reset(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: hardware reset failed\n", dev->name);
+		goto fail;
+	}
+
+	retval = adm8211_init_rings(dev);
+	if (retval) {
+		printk(KERN_ERR "%s: failed to initialize rings\n", dev->name);
+		goto fail;
+	}
+
+	adm8211_hw_init(dev);
+	adm8211_rf_set_channel(dev, priv->channel);
+
+	ADM8211_CSR_WRITE(IER, __constant_cpu_to_le32(ADM8211_INTMASK));
+	adm8211_update_mode(dev);
+	ADM8211_CSR_WRITE(RDR, 0);
+
+fail:
+	return retval;
+}
+
+static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, 
int len,
+				   int plcp_signal, int short_preamble)
+{
+	/* Alternative calculation from NetBSD: */
+
+/* IEEE 802.11b durations for DSSS PHY in microseconds */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE	144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE	72
+#define IEEE80211_DUR_DS_FAST_PLCPHDR	24
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR	48
+#define IEEE80211_DUR_DS_SLOW_ACK	112
+#define IEEE80211_DUR_DS_FAST_ACK	56
+#define IEEE80211_DUR_DS_SLOW_CTS	112
+#define IEEE80211_DUR_DS_FAST_CTS	56
+#define IEEE80211_DUR_DS_SLOT		20
+#define IEEE80211_DUR_DS_SIFS		10
+
+	int remainder;
+
+	*dur = (80 * (24 + payload_len) + plcp_signal - 1)
+		/ plcp_signal;
+
+	if (plcp_signal <= PLCP_SIGNAL_2M)
+		/* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK;
+	else
+		/* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */
+		*dur += 3 * (IEEE80211_DUR_DS_SIFS +
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE +
+			     IEEE80211_DUR_DS_FAST_PLCPHDR) +
+			     IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK;
+
+	/* lengthen duration if long preamble */
+	if (!short_preamble)
+		*dur +=	3 * (IEEE80211_DUR_DS_LONG_PREAMBLE -
+			     IEEE80211_DUR_DS_SHORT_PREAMBLE) +
+			3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR -
+			     IEEE80211_DUR_DS_FAST_PLCPHDR);
+
+
+	*plcp = (80 * len) / plcp_signal;
+	remainder = (80 * len) % plcp_signal;
+	if (plcp_signal == PLCP_SIGNAL_11M &&
+	    remainder <= 30 && remainder > 0)
+		*plcp = (*plcp | 0x8000) + 1;
+	else if (remainder)
+		(*plcp)++;
+}
+
+/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */
+static void adm8211_tx_raw(struct net_device *dev, struct sk_buff *skb,
+			   u16 plcp_signal, struct ieee80211_tx_control *control, int unicast)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned long flags;
+	dma_addr_t mapping;
+	unsigned entry;
+	u32 flag;
+
+	mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+				 PCI_DMA_TODEVICE);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (priv->cur_tx - priv->dirty_tx < priv->tx_ring_size / 2)
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2)
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else if (priv->cur_tx - priv->dirty_tx < priv->tx_ring_size - 2)
+		flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+	else {
+		flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS;
+		printk(KERN_DEBUG "%s: Yikes, this shouldn't happen!\n", dev->name);
+		netif_stop_queue(dev);
+	}
+
+	entry = priv->cur_tx % priv->tx_ring_size;
+
+	priv->tx_buffers[entry].skb = skb;
+	priv->tx_buffers[entry].mapping = mapping;
+	memcpy(&priv->tx_buffers[entry].tx_status.control, control, 
sizeof(*control));
+	priv->tx_buffers[entry].unicast = unicast;
+	priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
+
+	if (entry == priv->tx_ring_size - 1)
+		flag |= TDES1_CONTROL_TER;
+	priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len);
+
+	/* Set TX rate (SIGNAL field in PLCP PPDU format) */
+	flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */;
+	priv->tx_ring[entry].status = cpu_to_le32(flag);
+
+	priv->cur_tx++;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Trigger transmit poll */
+	ADM8211_CSR_WRITE(TDR, 0);
+
+	dev->trans_start = jiffies;
+}
+
+/* Put adm8211_tx_hdr on skb and transmit */
+static int adm8211_tx(struct net_device *dev, struct sk_buff *skb,
+		      struct ieee80211_tx_control *control)
+{
+	struct adm8211_tx_hdr *txhdr;
+	struct ieee80211_hdr *hdr;
+	u16 fc_;
+	__le16 fc;
+	u8 dst[ETH_ALEN];
+	size_t payload_len, hdrlen;
+	int plcp, dur, len;
+	int plcp_signal;
+	int short_preamble;
+
+	if (control->tx_rate < 0) {
+		short_preamble = 1;
+		plcp_signal = -control->tx_rate;
+	} else {
+		short_preamble = 0;
+		plcp_signal = control->tx_rate;
+	}
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+	fc  = hdr->frame_control;
+	fc_ = le16_to_cpu(fc);
+	memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
+
+	hdrlen = ieee80211_get_hdrlen(fc_);
+	skb_pull(skb, hdrlen);
+	fc_ &= ~WLAN_FC_ISWEP;
+	payload_len = skb->len;
+
+	if (skb_headroom(skb) < sizeof(struct adm8211_tx_hdr)) {
+		if (pskb_expand_head(skb, sizeof(struct adm8211_tx_hdr), 0, GFP_ATOMIC)) {
+			printk(KERN_DEBUG "%s: failed to allocate room for TX "
+			       "header\n", dev->name);
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr));
+	memset(txhdr, 0, sizeof(*txhdr));
+	memcpy(txhdr->da, dst, ETH_ALEN);
+	txhdr->signal = plcp_signal;
+	txhdr->frame_body_size = cpu_to_le16(payload_len);
+	txhdr->frame_control = fc;
+
+	len = hdrlen + payload_len + FCS_LEN;
+	if (fc_ & WLAN_FC_ISWEP)
+		len += 8;
+
+	txhdr->frag = cpu_to_le16(0x0FFF);
+	adm8211_calc_durations(&dur, &plcp, payload_len,
+			       len, plcp_signal, short_preamble);
+	txhdr->plcp_frag_head_len = cpu_to_le16(plcp);
+	txhdr->plcp_frag_tail_len = cpu_to_le16(plcp);
+	txhdr->dur_frag_head = cpu_to_le16(dur);
+	txhdr->dur_frag_tail = cpu_to_le16(dur);
+
+	txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER);
+
+	if (short_preamble)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
+
+	if (control->use_rts_cts)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
+
+	if (fc_ & WLAN_FC_ISWEP)
+		txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE);
+
+	txhdr->retry_limit = control->retry_limit;
+
+	adm8211_tx_raw(dev, skb, plcp_signal, 
control, !is_multicast_ether_addr(dst));
+
+	return NETDEV_TX_OK;
+}
+
+static int adm8211_alloc_rings(struct net_device *dev)
+{
+	struct adm8211_priv *priv = ieee80211_dev_hw_data(dev);
+	unsigned int ring_size;
+
+	priv->rx_buffers = kmalloc(sizeof(struct adm8211_ring_info) *
+				(priv->rx_ring_size + priv->tx_ring_size), GFP_KERNEL);
+	if (!priv->rx_buffers)
+		return -ENOMEM;
+	
+	priv->tx_buffers = ((void *)priv->rx_buffers) + sizeof(struct 
adm8211_ring_info) * priv->tx_ring_size;
+
+	/* Allocate TX/RX descriptors */
+	ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size +
+		    sizeof(struct adm8211_desc) * priv->tx_ring_size;
+	priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size,
+					     &priv->rx_ring_dma);
+
+	if (!priv->rx_ring) {
+		kfree(priv->rx_buffers);
+		priv->rx_buffers = priv->tx_buffers = NULL;
+		return -ENOMEM;
+	}
+
+	priv->tx_ring = (struct adm8211_desc *) (priv->rx_ring + 
priv->rx_ring_size);
+	priv->tx_ring_dma = priv->rx_ring_dma +
+		sizeof(struct adm8211_desc) * priv->rx_ring_size;
+
+	return 0;
+}
+
+static int __devinit adm8211_probe(struct pci_dev *pdev,
+				   const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct ieee80211_hw *hw;
+	struct adm8211_priv *priv;
+	unsigned long mem_addr, mem_len;
+	unsigned int io_addr, io_len;
+	int err;
+	u32 reg;
+
+#ifndef MODULE
+	static unsigned int cardidx;
+	if (!cardidx++)
+		printk(version);
+#endif
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", 
pci_name(pdev));
+		return err;
+	}
+
+	io_addr = pci_resource_start(pdev, 0);
+	io_len = pci_resource_len(pdev, 0);
+	mem_addr = pci_resource_start(pdev, 1);
+	mem_len = pci_resource_len(pdev, 1);
+	if (io_len < 256 || mem_len < 1024) {
+		printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev));
+		goto err_disable_pdev;
+	}
+
+
+	/* check signature */
+	pci_read_config_dword(pdev, 0x80 /* CR32 */, &reg);
+	if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) {
+		printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), 
reg);
+		goto err_disable_pdev;
+	}
+
+	err = pci_request_regions(pdev, "adm8211");
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", 
pci_name(pdev));
+		return err; /* someone else grabbed it? don't disable it */
+	}
+
+	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) ||
+	    pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
+		printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", 
pci_name(pdev));
+		goto err_free_reg;
+	}
+
+	pci_set_master(pdev);
+
+	dev = ieee80211_alloc_hw(sizeof(*priv), NULL);
+	if (!dev) {
+		printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", pci_name(pdev));
+		err = -ENOMEM;
+		goto err_free_reg;
+	}
+	priv = ieee80211_dev_hw_data(dev);
+	hw = &priv->hw;
+	priv->pdev = pdev;
+
+	spin_lock_init(&priv->lock);
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	pci_set_drvdata(pdev, dev);
+	priv->msg_enable = netif_msg_init(debug, NETIF_MSG_DRV | NETIF_MSG_PROBE);
+
+	priv->map = pci_iomap(pdev, 1, mem_len);
+	if (!priv->map)
+		priv->map = pci_iomap(pdev, 0, io_len);
+
+	if (!priv->map) {
+		printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", 
pci_name(pdev));
+		goto err_free_dev;
+	}
+
+	dev->mem_start = mem_addr;
+	dev->mem_end = mem_addr + mem_len;
+	dev->base_addr = io_addr;
+	dev->irq = pdev->irq;
+
+	priv->rx_ring_size = rx_ring_size;
+	priv->tx_ring_size = tx_ring_size;
+	
+	if (adm8211_alloc_rings(dev)) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", 
pci_name(pdev));
+		goto err_iounmap;
+	}
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &priv->revid);
+
+	put_unaligned(ADM8211_CSR_READ(PAR0), (u32 *) dev->dev_addr);
+	put_unaligned(ADM8211_CSR_READ(PAR1) & (__force u32) __constant_cpu_to_le32
(0xffff),
+		      (u16 *) &dev->dev_addr[4]);
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING "%s (adm8211): Invalid hwaddr! Using randomly generated 
hwaddr\n", pci_name(pdev));
+		random_ether_addr(dev->dev_addr);
+	}
+
+	hw->version = 2;
+	hw->name = "adm8211";
+	hw->host_gen_beacon = 0;
+	hw->device_hides_wep = 0;
+	hw->rx_includes_fcs = 0;	// however, FCS is kept in promisc mode
+	hw->host_broadcast_ps_buffering = 0;
+	hw->wep_include_iv = 1;
+	hw->data_nullfunc_ack = 0;
+	hw->no_tkip_wmm_hwaccel = 1;
+	hw->extra_hdr_room = 0;
+	hw->device_strips_mic = 0;
+	hw->monitor_during_oper = 0;
+	hw->fraglist = 0;
+	hw->channel_change_time = 1000;
+
+	hw->num_modes = 1;
+	hw->modes = priv->modes;
+	priv->modes[0].mode = MODE_IEEE80211B;
+	/* channel info filled in by adm8211_read_eeprom */
+	memcpy(priv->rates, adm8211_rates, sizeof(adm8211_rates));
+	priv->modes[0].num_rates = ARRAY_SIZE(adm8211_rates);
+	priv->modes[0].rates = priv->rates;
+
+	hw->tx = adm8211_tx;
+	hw->reset = adm8211_reset;
+	hw->open = adm8211_open;
+	hw->stop = adm8211_stop;
+	hw->add_interface = adm8211_add_interface;
+	hw->remove_interface = adm8211_remove_interface;
+
+	hw->config = adm8211_config;
+	hw->config_interface = adm8211_config_interface;
+	hw->set_multicast_list = adm8211_set_rx_mode;
+	hw->passive_scan = adm8211_scan;
+	hw->get_stats = adm8211_get_stats;
+	hw->get_tsf = adm8211_get_tsft;
+	hw->get_tx_stats = adm8211_get_tx_stats;
+	hw->queues = 1;		// ADM8211C supports more, maybe ADM8211B
+
+	priv->retry_limit = 3;
+	priv->ant_power = 0x40;
+	priv->tx_power = 0x40;
+	priv->lpf_cutoff = 0xFF;
+	priv->lnags_threshold = 0xFF;
+
+	/* Power-on issue. EEPROM won't read correctly without */
+	if (priv->revid >= ADM8211_REV_BA) {
+		ADM8211_CSR_WRITE(FRCTL, 0);
+		ADM8211_CSR_READ(FRCTL);
+		ADM8211_CSR_WRITE(FRCTL, 1);
+		ADM8211_CSR_READ(FRCTL);
+		mdelay(100);
+	}
+
+	err = adm8211_read_eeprom(dev);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot allocate eeprom buffer\n", 
pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	priv->channel = priv->modes[0].channels[0].chan;
+
+	err = ieee80211_register_hw(dev, &priv->hw);
+	if (err) {
+		printk(KERN_ERR "%s (adm8211): Cannot register netdevice\n", 
pci_name(pdev));
+		goto err_free_desc;
+	}
+
+	printk("%s: hwaddr " MAC_FMT ", Rev 0x%02x\n",
+	       dev->name, MAC_ARG(dev->dev_addr), priv->revid);
+
+	return 0;
+
+ err_free_desc:
+	pci_free_consistent(pdev,
+			    sizeof(struct adm8211_desc) * priv->rx_ring_size +
+			    sizeof(struct adm8211_desc) * priv->tx_ring_size,
+			    priv->rx_ring, priv->rx_ring_dma);
+	kfree(priv->rx_buffers);
+
+ err_iounmap:
+	pci_iounmap(pdev, priv->map);
+
+ err_free_dev:
+	pci_set_drvdata(pdev, NULL);
+	ieee80211_free_hw(dev);
+
+ err_free_reg:
+	pci_release_regions(pdev);
+
+ err_disable_pdev:
+	pci_disable_device(pdev);
+	return err;
+}
+
+
+static void __devexit adm8211_remove(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct adm8211_priv *priv;
+
+	if (!dev)
+		return;
+
+	ieee80211_unregister_hw(dev);
+
+	priv = ieee80211_dev_hw_data(dev);
+
+	pci_free_consistent(pdev,
+			    sizeof(struct adm8211_desc) * priv->rx_ring_size +
+			    sizeof(struct adm8211_desc) * priv->tx_ring_size,
+			    priv->rx_ring, priv->rx_ring_dma);
+
+	kfree(priv->rx_buffers);
+	kfree(priv->eeprom);
+	pci_iounmap(pdev, priv->map);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	ieee80211_free_hw(dev);
+}
+
+
+#ifdef CONFIG_PM
+static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	netif_device_detach(dev);
+
+	if (dev->flags & IFF_UP)
+		dev->stop(dev);
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, PCI_D3hot);
+	return 0;
+}
+
+static int adm8211_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	if (dev->flags & IFF_UP)
+		dev->open(dev);
+
+	netif_device_attach(dev);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+
+MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);
+
+/* TODO: enable_wake */
+static struct pci_driver adm8211_driver = {
+	.name		= "adm8211",
+	.id_table	= adm8211_pci_id_table,
+	.probe		= adm8211_probe,
+	.remove		= __devexit_p(adm8211_remove),
+#ifdef CONFIG_PM
+	.suspend	= adm8211_suspend,
+	.resume		= adm8211_resume,
+#endif /* CONFIG_PM */
+};
+
+
+
+static int __init adm8211_init(void)
+{
+#ifdef MODULE
+	printk(version);
+#endif
+
+	return pci_register_driver(&adm8211_driver);
+}
+
+
+static void __exit adm8211_exit(void)
+{
+	pci_unregister_driver(&adm8211_driver);
+}
+
+
+module_init(adm8211_init);
+module_exit(adm8211_exit);
diff --git a/drivers/net/wireless/d80211/adm8211/adm8211.h 
b/drivers/net/wireless/d80211/adm8211/adm8211.h
new file mode 100644
index 0000000..c215013
--- /dev/null
+++ b/drivers/net/wireless/d80211/adm8211/adm8211.h
@@ -0,0 +1,631 @@
+#ifndef ADM8211_H
+#define ADM8211_H
+
+/* ADM8211 Registers */
+
+/* CR32 (SIG) signature */
+#define ADM8211_SIG1		0x82011317 /* ADM8211A */
+#define ADM8211_SIG2		0x82111317 /* ADM8211B/ADM8211C */
+
+#define ADM8211_CSR_READ(r) ioread32(&priv->map->r)
+#define ADM8211_CSR_WRITE(r, val) iowrite32((__force u32)(val), 
&priv->map->r)
+
+/* CSR (Host Control and Status Registers) */
+struct adm8211_csr {
+	__le32 PAR;		/* 0x00 CSR0 */
+	__le32 FRCTL;		/* 0x04 CSR0A */
+	__le32 TDR;		/* 0x08 CSR1 */
+	__le32 WTDP;		/* 0x0C CSR1A */
+	__le32 RDR;		/* 0x10 CSR2 */
+	__le32 WRDP;		/* 0x14 CSR2A */
+	__le32 RDB;		/* 0x18 CSR3 */
+	__le32 TDBH;		/* 0x1C CSR3A */
+	__le32 TDBD;		/* 0x20 CSR4 */
+	__le32 TDBP;		/* 0x24 CSR4A */
+	__le32 STSR;		/* 0x28 CSR5 */
+	__le32 TDBB;		/* 0x2C CSR5A */
+	__le32 NAR;		/* 0x30 CSR6 */
+	__le32 CSR6A;		/* reserved */
+	__le32 IER;		/* 0x38 CSR7 */
+	__le32 TKIPSCEP;	/* 0x3C CSR7A */	
+	__le32 LPC;		/* 0x40 CSR8 */
+	__le32 CSR_TEST1;	/* 0x44 CSR8A */
+	__le32 SPR;		/* 0x48 CSR9 */
+	__le32 CSR_TEST0;	/* 0x4C CSR9A */
+	__le32 WCSR;		/* 0x50 CSR10 */
+	__le32 WPDR;		/* 0x54 CSR10A */
+	__le32 GPTMR;		/* 0x58 CSR11 */
+	__le32 GPIO;		/* 0x5C CSR11A */
+	__le32 BBPCTL;		/* 0x60 CSR12 */
+	__le32 SYNCTL;		/* 0x64 CSR12A */
+	__le32 PLCPHD;		/* 0x68 CSR13 */
+	__le32 MMIWA;		/* 0x6C CSR13A */
+	__le32 MMIRD0;		/* 0x70 CSR14 */
+	__le32 MMIRD1;		/* 0x74 CSR14A */
+	__le32 TXBR;		/* 0x78 CSR15 */
+	__le32 SYNDATA;		/* 0x7C CSR15A */
+	__le32 ALCS;		/* 0x80 CSR16 */
+	__le32 TOFS2;		/* 0x84 CSR17 */
+	__le32 CMDR;		/* 0x88 CSR18 */
+	__le32 PCIC;		/* 0x8C CSR19 */
+	__le32 PMCSR;		/* 0x90 CSR20 */
+	__le32 PAR0;		/* 0x94 CSR21 */
+	__le32 PAR1;		/* 0x98 CSR22 */
+	__le32 MAR0;		/* 0x9C CSR23 */
+	__le32 MAR1;		/* 0xA0 CSR24 */
+	__le32 ATIMDA0;		/* 0xA4 CSR25 */
+	__le32 ABDA1;		/* 0xA8 CSR26 */
+	__le32 BSSID0;		/* 0xAC CSR27 */
+	__le32 TXLMT;		/* 0xB0 CSR28 */
+	__le32 MIBCNT;		/* 0xB4 CSR29 */
+	__le32 BCNT;		/* 0xB8 CSR30 */
+	__le32 TSFTH;		/* 0xBC CSR31 */
+	__le32 TSC;		/* 0xC0 CSR32 */
+	__le32 SYNRF;		/* 0xC4 CSR33 */
+	__le32 BPLI;		/* 0xC8 CSR34 */
+	__le32 CAP0;		/* 0xCC CSR35 */
+	__le32 CAP1;		/* 0xD0 CSR36 */
+	__le32 RMD;		/* 0xD4 CSR37 */
+	__le32 CFPP;		/* 0xD8 CSR38 */
+	__le32 TOFS0;		/* 0xDC CSR39 */
+	__le32 TOFS1;		/* 0xE0 CSR40 */
+	__le32 IFST;		/* 0xE4 CSR41 */
+	__le32 RSPT;		/* 0xE8 CSR42 */
+	__le32 TSFTL;		/* 0xEC CSR43 */
+	__le32 WEPCTL;		/* 0xF0 CSR44 */
+	__le32 WESK;		/* 0xF4 CSR45 */
+	__le32 WEPCNT;		/* 0xF8 CSR46 */
+	__le32 MACTEST;		/* 0xFC CSR47 */
+	__le32 FER;		/* 0x100 */
+	__le32 FEMR;		/* 0x104 */
+	__le32 FPSR;		/* 0x108 */
+	__le32 FFER;		/* 0x10C */
+} __attribute__ ((packed));
+
+/* CSR0 - PAR (PCI Address Register) */
+#define ADM8211_PAR_MWIE	(1 << 24)
+#define ADM8211_PAR_MRLE	(1 << 23)
+#define ADM8211_PAR_MRME	(1 << 21)
+#define ADM8211_PAR_RAP		((1 << 18) | (1 << 17))
+#define ADM8211_PAR_CAL		((1 << 15) | (1 << 14))
+#define ADM8211_PAR_PBL		0x00003f00
+#define ADM8211_PAR_BLE		(1 << 7)
+#define ADM8211_PAR_DSL		0x0000007c
+#define ADM8211_PAR_BAR		(1 << 1)
+#define ADM8211_PAR_SWR		(1 << 0)
+
+/* CSR1 - FRCTL (Frame Control Register) */
+#define ADM8211_FRCTL_PWRMGT	(1 << 31)
+#define ADM8211_FRCTL_MAXPSP	(1 << 27)
+#define ADM8211_FRCTL_DRVPRSP	(1 << 26)
+#define ADM8211_FRCTL_DRVBCON	(1 << 25)
+#define ADM8211_FRCTL_AID	0x0000ffff
+#define ADM8211_FRCTL_AID_ON	0x0000c000
+
+/* CSR5 - STSR (Status Register) */
+#define ADM8211_STSR_PCF	(1 << 31)
+#define ADM8211_STSR_BCNTC	(1 << 30)
+#define ADM8211_STSR_GPINT	(1 << 29)
+#define ADM8211_STSR_LinkOff	(1 << 28)
+#define ADM8211_STSR_ATIMTC	(1 << 27)
+#define ADM8211_STSR_TSFTF	(1 << 26)
+#define ADM8211_STSR_TSCZ	(1 << 25)
+#define ADM8211_STSR_LinkOn	(1 << 24)
+#define ADM8211_STSR_SQL	(1 << 23)
+#define ADM8211_STSR_WEPTD	(1 << 22)
+#define ADM8211_STSR_ATIME	(1 << 21)
+#define ADM8211_STSR_TBTT	(1 << 20)
+#define ADM8211_STSR_NISS	(1 << 16)
+#define ADM8211_STSR_AISS	(1 << 15)
+#define ADM8211_STSR_TEIS	(1 << 14)
+#define ADM8211_STSR_FBE	(1 << 13)
+#define ADM8211_STSR_REIS	(1 << 12)
+#define ADM8211_STSR_GPTT	(1 << 11)
+#define ADM8211_STSR_RPS	(1 << 8)
+#define ADM8211_STSR_RDU	(1 << 7)
+#define ADM8211_STSR_RCI	(1 << 6)
+#define ADM8211_STSR_TUF	(1 << 5)
+#define ADM8211_STSR_TRT	(1 << 4)
+#define ADM8211_STSR_TLT	(1 << 3)
+#define ADM8211_STSR_TDU	(1 << 2)
+#define ADM8211_STSR_TPS	(1 << 1)
+#define ADM8211_STSR_TCI	(1 << 0)
+
+/* CSR6 - NAR (Network Access Register) */
+#define ADM8211_NAR_TXCF	(1 << 31)
+#define ADM8211_NAR_HF		(1 << 30)
+#define ADM8211_NAR_UTR		(1 << 29)
+#define ADM8211_NAR_SQ		(1 << 28)
+#define ADM8211_NAR_CFP		(1 << 27)
+#define ADM8211_NAR_SF		(1 << 21)
+#define ADM8211_NAR_TR		((1 << 15) | (1 << 14))
+#define ADM8211_NAR_ST		(1 << 13)
+#define ADM8211_NAR_OM		((1 << 11) | (1 << 10))
+#define ADM8211_NAR_MM		(1 << 7)
+#define ADM8211_NAR_PR		(1 << 6)
+#define ADM8211_NAR_EA		(1 << 5)
+#define ADM8211_NAR_PB		(1 << 3)
+#define ADM8211_NAR_STPDMA	(1 << 2)
+#define ADM8211_NAR_SR		(1 << 1)
+#define ADM8211_NAR_CTX		(1 << 0)
+
+#define ADM8211_IDLE()		do { \
+	if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) {\
+		ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~(ADM8211_NAR_SR | 
ADM8211_NAR_ST)));\
+		ADM8211_CSR_READ(NAR);\
+		mdelay(20);\
+	}\
+} while (0)
+
+#define ADM8211_IDLE_RX()	do { \
+	if (priv->nar & ADM8211_NAR_SR) {\
+		ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~ADM8211_NAR_SR));\
+		ADM8211_CSR_READ(NAR);\
+		mdelay(20);\
+	}\
+} while (0)
+
+#define ADM8211_IDLE_TX()	do { \
+	if (priv->nar & ADM8211_NAR_ST) {\
+		ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~ADM8211_NAR_ST));\
+		ADM8211_CSR_READ(NAR);\
+		mdelay(20);\
+	}\
+} while (0)
+
+#define ADM8211_RESTORE()	do { \
+	if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \
+		ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar));\
+} while (0)
+
+/* CSR7 - IER (Interrupt Enable Register) */
+#define ADM8211_IER_PCFIE	(1 << 31)
+#define ADM8211_IER_BCNTCIE	(1 << 30)
+#define ADM8211_IER_GPIE	(1 << 29)
+#define ADM8211_IER_LinkOffIE	(1 << 28)
+#define ADM8211_IER_ATIMTCIE	(1 << 27)
+#define ADM8211_IER_TSFTFIE	(1 << 26)
+#define ADM8211_IER_TSCZE	(1 << 25)
+#define ADM8211_IER_LinkOnIE	(1 << 24)
+#define ADM8211_IER_SQLIE	(1 << 23)
+#define ADM8211_IER_WEPIE	(1 << 22)
+#define ADM8211_IER_ATIMEIE	(1 << 21)
+#define ADM8211_IER_TBTTIE	(1 << 20)
+#define ADM8211_IER_NIE		(1 << 16)
+#define ADM8211_IER_AIE		(1 << 15)
+#define ADM8211_IER_TEIE	(1 << 14)
+#define ADM8211_IER_FBEIE	(1 << 13)
+#define ADM8211_IER_REIE	(1 << 12)
+#define ADM8211_IER_GPTIE	(1 << 11)
+#define ADM8211_IER_RSIE	(1 << 8)
+#define ADM8211_IER_RUIE	(1 << 7)
+#define ADM8211_IER_RCIE	(1 << 6)
+#define ADM8211_IER_TUIE	(1 << 5)
+#define ADM8211_IER_TRTIE	(1 << 4)
+#define ADM8211_IER_TLTTIE	(1 << 3)
+#define ADM8211_IER_TDUIE	(1 << 2)
+#define ADM8211_IER_TPSIE	(1 << 1)
+#define ADM8211_IER_TCIE	(1 << 0)
+
+/* CSR9 - SPR (Serial Port Register) */
+#define ADM8211_SPR_SRS		(1 << 11)
+#define ADM8211_SPR_SDO		(1 << 3)
+#define ADM8211_SPR_SDI		(1 << 2)
+#define ADM8211_SPR_SCLK	(1 << 1)
+#define ADM8211_SPR_SCS		(1 << 0)
+
+/* CSR9A - CSR_TEST0 */
+#define ADM8211_CSR_TEST0_EPNE	(1 << 18)
+#define ADM8211_CSR_TEST0_EPSNM	(1 << 17)
+#define ADM8211_CSR_TEST0_EPTYP	(1 << 16)
+#define ADM8211_CSR_TEST0_EPRLD	(1 << 15)
+
+/* CSR10 - WCSR (Wake-up Control/Status Register) */
+#define ADM8211_WCSR_CRCT	(1 << 30)
+#define ADM8211_WCSR_TSFTWE	(1 << 20)
+#define ADM8211_WCSR_TIMWE	(1 << 19)
+#define ADM8211_WCSR_ATIMWE	(1 << 18)
+#define ADM8211_WCSR_KEYWE	(1 << 17)
+#define ADM8211_WCSR_MPRE	(1 << 9)
+#define ADM8211_WCSR_LSOE	(1 << 8)
+#define ADM8211_WCSR_KEYUP	(1 << 6)
+#define ADM8211_WCSR_TSFTW	(1 << 5)
+#define ADM8211_WCSR_TIMW	(1 << 4)
+#define ADM8211_WCSR_ATIMW	(1 << 3)
+#define ADM8211_WCSR_MPR	(1 << 1)
+#define ADM8211_WCSR_LSO	(1 << 0)
+
+/* CSR11A - GPIO */
+#define ADM8211_CSR_GPIO_EN5	(1 << 17)
+#define ADM8211_CSR_GPIO_EN4	(1 << 16)
+#define ADM8211_CSR_GPIO_EN3	(1 << 15)
+#define ADM8211_CSR_GPIO_EN2	(1 << 14)
+#define ADM8211_CSR_GPIO_EN1	(1 << 13)
+#define ADM8211_CSR_GPIO_EN0	(1 << 12)
+#define ADM8211_CSR_GPIO_O5	(1 << 11)
+#define ADM8211_CSR_GPIO_O4	(1 << 10)
+#define ADM8211_CSR_GPIO_O3	(1 << 9)
+#define ADM8211_CSR_GPIO_O2	(1 << 8)
+#define ADM8211_CSR_GPIO_O1	(1 << 7)
+#define ADM8211_CSR_GPIO_O0	(1 << 6)
+#define ADM8211_CSR_GPIO_IN	0x0000003f
+
+/* CSR12 - BBPCTL (BBP Control port) */
+#define ADM8211_BBPCTL_MMISEL	(1 << 31)
+#define ADM8211_BBPCTL_SPICADD  (0x7F << 24)
+#define ADM8211_BBPCTL_RF3000	(0x20 << 24)
+#define ADM8211_BBPCTL_TXCE	(1 << 23)
+#define ADM8211_BBPCTL_RXCE	(1 << 22)
+#define ADM8211_BBPCTL_CCAP	(1 << 21)
+#define ADM8211_BBPCTL_TYPE	0x001c0000
+#define ADM8211_BBPCTL_WR	(1 << 17)
+#define ADM8211_BBPCTL_RD	(1 << 16)
+#define ADM8211_BBPCTL_ADDR	0x0000ff00
+#define ADM8211_BBPCTL_DATA	0x000000ff
+
+/* CSR12A - SYNCTL (Synthesizer Control port) */
+#define ADM8211_SYNCTL_WR	(1 << 31)
+#define ADM8211_SYNCTL_RD	(1 << 30)
+#define ADM8211_SYNCTL_CS0	(1 << 29)
+#define ADM8211_SYNCTL_CS1	(1 << 28)
+#define ADM8211_SYNCTL_CAL	(1 << 27)
+#define ADM8211_SYNCTL_SELCAL	(1 << 26)
+#define ADM8211_SYNCTL_RFtype	((1 << 24) || (1 << 23) || (1 << 22))
+#define ADM8211_SYNCTL_RFMD	(1 << 22)
+#define ADM8211_SYNCTL_GENERAL	(0x7 << 22)
+/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */
+
+/* CSR18 - CMDR (Command Register) */
+#define ADM8211_CMDR_PM		(1 << 19)
+#define ADM8211_CMDR_APM	(1 << 18)
+#define ADM8211_CMDR_RTE	(1 << 4)
+#define ADM8211_CMDR_DRT	((1 << 3) | (1 << 2))
+#define ADM8211_CMDR_DRT_8DW	(0x0 << 2)
+#define ADM8211_CMDR_DRT_16DW	(0x1 << 2)
+#define ADM8211_CMDR_DRT_SF	(0x2 << 2)
+
+/* CSR33 - SYNRF (SYNRF direct control) */
+#define ADM8211_SYNRF_SELSYN	(1 << 31)
+#define ADM8211_SYNRF_SELRF	(1 << 30)
+#define ADM8211_SYNRF_LERF	(1 << 29)
+#define ADM8211_SYNRF_LEIF	(1 << 28)
+#define ADM8211_SYNRF_SYNCLK	(1 << 27)
+#define ADM8211_SYNRF_SYNDATA	(1 << 26)
+#define ADM8211_SYNRF_PE1	(1 << 25)
+#define ADM8211_SYNRF_PE2	(1 << 24)
+#define ADM8211_SYNRF_PA_PE	(1 << 23)
+#define ADM8211_SYNRF_TR_SW	(1 << 22)
+#define ADM8211_SYNRF_TR_SWN	(1 << 21)
+#define ADM8211_SYNRF_RADIO	(1 << 20)
+#define ADM8211_SYNRF_CAL_EN	(1 << 19)
+#define ADM8211_SYNRF_PHYRST	(1 << 18)
+
+#define ADM8211_SYNRF_IF_SELECT_0 	(1 << 31)
+#define ADM8211_SYNRF_IF_SELECT_1 	((1 << 31) | (1 << 28))
+#define ADM8211_SYNRF_WRITE_SYNDATA_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_SYNDATA_1	((1 << 31) | (1 << 26))
+#define ADM8211_SYNRF_WRITE_CLOCK_0	(1 << 31)
+#define ADM8211_SYNRF_WRITE_CLOCK_1	((1 << 31) | (1 << 27))
+
+/* CSR44 - WEPCTL (WEP Control) */
+#define ADM8211_WEPCTL_WEPENABLE   (1 << 31)
+#define ADM8211_WEPCTL_WPAENABLE   (1 << 30)
+#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29)
+#define ADM8211_WEPCTL_TABLE_WR	(1 << 28)
+#define ADM8211_WEPCTL_TABLE_RD	(1 << 27)
+#define ADM8211_WEPCTL_WEPRXBYP	(1 << 25)
+#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23)
+#define ADM8211_WEPCTL_ADDR	(0x000001ff)
+
+/* CSR45 - WESK (Data Entry for Share/Individual Key) */
+#define ADM8211_WESK_DATA	(0x0000ffff)
+
+/* FER (Function Event Register) */
+#define ADM8211_FER_INTR_EV_ENT	(1 << 15)
+
+
+/* Si4126 RF Synthesizer - Control Registers */
+#define SI4126_MAIN_CONF	0
+#define SI4126_PHASE_DET_GAIN	1
+#define SI4126_POWERDOWN	2
+#define SI4126_RF1_N_DIV	3 /* only Si4136 */
+#define SI4126_RF2_N_DIV	4
+#define SI4126_IF_N_DIV		5
+#define SI4126_RF1_R_DIV	6 /* only Si4136 */
+#define SI4126_RF2_R_DIV	7
+#define SI4126_IF_R_DIV		8
+
+/* Main Configuration */
+#define SI4126_MAIN_XINDIV2	(1 << 6)
+#define SI4126_MAIN_IFDIV	((1 << 11) | (1 << 10))
+/* Powerdown */
+#define SI4126_POWERDOWN_PDIB	(1 << 1)
+#define SI4126_POWERDOWN_PDRB	(1 << 0)
+
+
+/* RF3000 BBP - Control Port Registers */
+/* 0x00 - reserved */
+#define RF3000_MODEM_CTRL__RX_STATUS 0x01
+#define RF3000_CCA_CTRL 0x02
+#define RF3000_DIVERSITY__RSSI 0x03
+#define RF3000_RX_SIGNAL_FIELD 0x04
+#define RF3000_RX_LEN_MSB 0x05
+#define RF3000_RX_LEN_LSB 0x06
+#define RF3000_RX_SERVICE_FIELD 0x07
+#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11
+#define RF3000_TX_LEN_MSB 0x12
+#define RF3000_TX_LEN_LSB 0x13
+#define RF3000_LOW_GAIN_CALIB 0x14
+#define RF3000_HIGH_GAIN_CALIB 0x15
+
+/* ADM8211 revisions */
+#define ADM8211_REV_AB 0x11
+#define ADM8211_REV_AF 0x15
+#define ADM8211_REV_BA 0x20
+#define ADM8211_REV_CA 0x30
+
+struct adm8211_desc {
+	__le32 status;
+	__le32 length;
+	__le32 buffer1;
+	__le32 buffer2;
+};
+
+#define RDES0_STATUS_OWN	(1 << 31)
+#define RDES0_STATUS_ES		(1 << 30)
+#define RDES0_STATUS_SQL	(1 << 29)
+#define RDES0_STATUS_DE		(1 << 28)
+#define RDES0_STATUS_FS		(1 << 27)
+#define RDES0_STATUS_LS		(1 << 26)
+#define RDES0_STATUS_PCF	(1 << 25)
+#define RDES0_STATUS_SFDE	(1 << 24)
+#define RDES0_STATUS_SIGE	(1 << 23)
+#define RDES0_STATUS_CRC16E	(1 << 22)
+#define RDES0_STATUS_RXTOE	(1 << 21)
+#define RDES0_STATUS_CRC32E	(1 << 20)
+#define RDES0_STATUS_ICVE	(1 << 19)
+#define RDES0_STATUS_DA1	(1 << 17)
+#define RDES0_STATUS_DA0	(1 << 16)
+#define RDES0_STATUS_RXDR	((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12))
+#define RDES0_STATUS_FL		(0x00000fff)
+
+#define RDES1_CONTROL_RER	(1 << 25)
+#define RDES1_CONTROL_RCH	(1 << 24)
+#define RDES1_CONTROL_RBS2	(0x00fff000)
+#define RDES1_CONTROL_RBS1	(0x00000fff)
+
+#define RDES1_STATUS_RSSI	(0x0000007f)
+
+
+#define TDES0_CONTROL_OWN	(1 << 31)
+#define TDES0_CONTROL_DONE	(1 << 30)
+#define TDES0_CONTROL_TXDR	(0x0ff00000)
+
+#define TDES0_STATUS_OWN	(1 << 31)
+#define TDES0_STATUS_DONE	(1 << 30)
+#define TDES0_STATUS_ES		(1 << 29)
+#define TDES0_STATUS_TLT	(1 << 28)
+#define TDES0_STATUS_TRT	(1 << 27)
+#define TDES0_STATUS_TUF	(1 << 26)
+#define TDES0_STATUS_TRO	(1 << 25)
+#define TDES0_STATUS_SOFBR	(1 << 24)
+#define TDES0_STATUS_ACR	(0x00000fff)
+
+#define TDES1_CONTROL_IC	(1 << 31)
+#define TDES1_CONTROL_LS	(1 << 30)
+#define TDES1_CONTROL_FS	(1 << 29)
+#define TDES1_CONTROL_TER	(1 << 25)
+#define TDES1_CONTROL_TCH	(1 << 24)
+#define TDES1_CONTROL_RBS2	(0x00fff000)
+#define TDES1_CONTROL_RBS1	(0x00000fff)
+
+/* SRAM offsets */
+#define ADM8211_SRAM(x) (priv->revid < ADM8211_REV_BA ? \
+        ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x)
+
+#define ADM8211_SRAM_INDIV_KEY   0x0000
+#define ADM8211_SRAM_A_SHARE_KEY 0x0160
+#define ADM8211_SRAM_B_SHARE_KEY 0x00c0
+
+#define ADM8211_SRAM_A_SSID      0x0180
+#define ADM8211_SRAM_B_SSID      0x00d4
+#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID)
+
+#define ADM8211_SRAM_A_SUPP_RATE 0x0191
+#define ADM8211_SRAM_B_SUPP_RATE 0x00dd
+#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE)
+
+#define ADM8211_SRAM_A_SIZE      0x0200
+#define ADM8211_SRAM_B_SIZE      0x01c0
+#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE)
+
+struct adm8211_ring_info {
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+	struct ieee80211_tx_status tx_status;
+	int unicast;
+};
+
+struct adm8211_eeprom {
+	__le16	signature;		/* 0x00 */
+	u8	major_version;		/* 0x02 */
+	u8	minor_version;		/* 0x03 */
+	u8	reserved_1[4];		/* 0x04 */
+	u8	hwaddr[6];		/* 0x08 */
+	u8	reserved_2[8];		/* 0x1E */
+	__le16	cr49;			/* 0x16 */
+	u8	cr03;			/* 0x18 */
+	u8	cr28;			/* 0x19 */
+	u8	cr29;			/* 0x1A */
+	u8	country_code;		/* 0x1B */
+
+/* specific bbp types */
+#define ADM8211_BBP_RFMD3000	0x00
+#define ADM8211_BBP_RFMD3002	0x01
+#define ADM8211_BBP_ADM8011	0x04
+	u8	specific_bbptype;	/* 0x1C */
+	u8	specific_rftype;	/* 0x1D */
+	u8	reserved_3[2];		/* 0x1E */
+	__le16	device_id;		/* 0x20 */
+	__le16	vendor_id;		/* 0x22 */
+	__le16	subsystem_id;		/* 0x24 */
+	__le16	subsystem_vendor_id;	/* 0x26 */
+	u8	maxlat;			/* 0x28 */
+	u8	mingnt;			/* 0x29 */
+	__le16	cis_pointer_low;	/* 0x2A */
+	__le16	cis_pointer_high;	/* 0x2C */
+	__le16	csr18;			/* 0x2E */
+	u8	reserved_4[16];		/* 0x30 */
+	u8	d1_pwrdara;		/* 0x40 */
+	u8	d0_pwrdara;		/* 0x41 */
+	u8	d3_pwrdara;		/* 0x42 */
+	u8	d2_pwrdara;		/* 0x43 */
+	u8	antenna_power[14];	/* 0x44 */
+	__le16	cis_wordcnt;		/* 0x52 */
+	u8	tx_power[14];		/* 0x54 */
+	u8	lpf_cutoff[14];		/* 0x62 */
+	u8	lnags_threshold[14];	/* 0x70 */
+	__le16	checksum;		/* 0x7E */
+	u8	cis_data[0];		/* 0x80, 384 bytes */
+} __attribute__ ((packed));
+
+static const struct ieee80211_rate adm8211_rates[] = {
+	{ .rate = 10,
+	  .val = 10,
+	  .val2 = -10,
+	  .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK },
+	{ .rate = 20,
+	  .val = 20,
+	  .val2 = -20,
+	  .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK_2 },
+	{ .rate = 55,
+	  .val = 55,
+	  .val2 = -55,
+	  .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK_2 },
+	{ .rate = 110,
+	  .val = 110,
+	  .val2 = -110,
+	  .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK_2 }
+};
+
+struct ieee80211_chan_range {
+	u8 min;
+	u8 max;
+};
+
+struct adm8211_priv {
+	struct pci_dev *pdev;
+	spinlock_t lock;
+	struct adm8211_csr __iomem *map;
+	struct adm8211_desc *rx_ring;
+	struct adm8211_desc *tx_ring;
+	dma_addr_t rx_ring_dma;
+	dma_addr_t tx_ring_dma;
+	struct adm8211_ring_info *rx_buffers;
+	struct adm8211_ring_info *tx_buffers;
+	unsigned rx_ring_size, tx_ring_size;
+	unsigned cur_tx, dirty_tx, cur_rx;
+
+	struct ieee80211_hw hw;
+	struct ieee80211_low_level_stats stats;
+	struct ieee80211_hw_modes modes[1];
+	struct ieee80211_rate rates[ARRAY_SIZE(adm8211_rates)];
+	int mode;
+
+	int channel;
+	u8 bssid[ETH_ALEN];
+	u8 ssid[32];
+	size_t ssid_len;
+
+	u32 msg_enable;
+
+	int (*eth_header_parse)(struct sk_buff *skb, unsigned char *haddr);
+
+	u8 soft_rx_crc;
+	u8 retry_limit;
+
+	u8 ant_power;
+	u8 tx_power;
+	u8 lpf_cutoff;
+	u8 lnags_threshold;
+	struct adm8211_eeprom *eeprom;
+	size_t eeprom_len;
+
+	u8 revid;
+
+	u32 nar;
+
+#define ADM8211_TYPE_INTERSIL	0x00
+#define ADM8211_TYPE_RFMD	0x01
+#define ADM8211_TYPE_MARVEL	0x02
+#define ADM8211_TYPE_AIROHA	0x03
+#define ADM8211_TYPE_ADMTEK     0x05
+	unsigned int rf_type:3;
+	unsigned int bbp_type:3;
+
+	u8 specific_bbptype;
+	enum {
+		ADM8211_RFMD2948 = 0x0,
+		ADM8211_RFMD2958 = 0x1,
+		ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2,
+		ADM8211_MAX2820 = 0x8,
+		ADM8211_AL2210L = 0xC,	/* Airoha */
+	} transceiver_type;
+};
+
+static const struct ieee80211_chan_range cranges[] = {
+	{1,  11},	/* FCC */
+	{1,  11},	/* IC */
+	{1,  13},	/* ETSI */
+	{10, 11},	/* SPAIN */
+	{10, 13},	/* FRANCE */
+	{14, 14},	/* MMK */
+	{1,  14},	/* MMK2 */
+};
+
+static const struct ieee80211_channel adm8211_channels[] = {
+	{ .chan = 1,
+	  .freq = 2412,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 2,
+	  .freq = 2417,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 3,
+	  .freq = 2422,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 4,
+	  .freq = 2427,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 5,
+	  .freq = 2432,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 6,
+	  .freq = 2437,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 7,
+	  .freq = 2442,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 8,
+	  .freq = 2447,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 9,
+	  .freq = 2452,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 10,
+	  .freq = 2457,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 11,
+	  .freq = 2462,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 12,
+	  .freq = 2467,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 13,
+	  .freq = 2472,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS},
+	{ .chan = 14,
+	  .freq = 2484,
+	  .flag = IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | 
IEEE80211_CHAN_W_IBSS}
+};
+
+#endif /* ADM8211_H */

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related

* ixgb EEH/PCI errors on reset
From: Linas Vepstas @ 2006-06-23 17:09 UTC (permalink / raw)
  To: Jesse Brandeburg, Ronciak, John, cramerj, jeffrey.t.kirsher,
	auke-jan.h.kok, ayyappan.veeraiyan
  Cc: netdev



Hi,

I've got another ixgb driver bug I'm struggling with; clues or hints
appreciated.

I've got a patch for PCI error recovery for the ixgb, which works on 
many older kernels but seems to be broken on linux-2.6.17-rc6-mm2
(which is ixgb version 1.0.109).  After performing a PCI reset on the 
card, I try to re-initialize the cad and the driver, with the following
sequence:

   pci_set_master(pdev);
   netif_carrier_off(netdev);
   netif_stop_queue(netdev);
   ixgb_check_options(adapter);
   ixgb_reset(adapter);

This is only a subset of the ixgb_probe code, since I don't need to 
request regions or do any of the other setup.  However, this code 
fails in an unexpected way.  The last call invokes ixgb_mac_reset()
which writes a reset bit, delays a few millisecs, and reads the reset
bit.  The problem I'm seeing is that the read 

  ctrl_reg = IXGB_READ_REG(hw, CTRL0);

triggers some PCI bus error that off-lines the device. Any hints
about where to look? This doesn't occur on other driver versions,
and doesn't occur on this driver during the ordinary probe() sequence.
Increasing the dealy doesn't seem to help. 

--linas



^ permalink raw reply

* Re: [openib-general] [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Grant Grundler @ 2006-06-23 17:14 UTC (permalink / raw)
  To: Arjan van de Ven
  Cc: Steve Wise, rdreier, linux-kernel, openib-general, netdev
In-Reply-To: <1151071471.3204.12.camel@laptopd505.fenrus.org>

On Fri, Jun 23, 2006 at 04:04:31PM +0200, Arjan van de Ven wrote:
> > I thought the posted write WILL eventually get to adapter memory.  Not
> > stall forever cached in a bridge.  I'm wrong?
> 
> I'm not sure there is a theoretical upper bound.... 

I'm not aware of one either since MMIO writes can travel
across many other chips that are not constrained by
PCI ordering rules (I'm thinking of SGI Altix...)

> (and if it's several msec per bridge, then you have a lot of latency
> anyway)

That's what my original concern was when I saw you point this out.
But MMIO reads here would be expensive and many drivers tolerate
this latency in exchange for avoiding the MMIO read in the
performance path.

grant

^ permalink raw reply

* Kernel Janitor TODO: dev_kfree_skb
From: John Daiker @ 2006-06-23 17:35 UTC (permalink / raw)
  To: netdev; +Cc: akpm, jgarzik

I was running through the kernel janitors TODO list 
(http://janitor.kernelnewbies.org/TODO) and thought I'd try to find 
something easy for my first attempt at kernel hacking/maintenance.

The "Balancing Function" header suggests that all net_device interrupt 
functions should use dev_kfree_skb_irq as opposed to dev_kfree_skb.
Should we ever be calling dev_kfree_skb directly, or would the better 
practice be to always call dev_kfree_skb_any (thus letting the kernel 
decide to call dev_kfree_skb or dev_kfree_skb_irq)?

dev_kfree_skb_any is defined in 'net/core/dev.c:1118'

John Daiker

^ permalink raw reply

* Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
From: Michael Krause @ 2006-06-23 18:02 UTC (permalink / raw)
  To: Grant Grundler, Arjan van de Ven
  Cc: netdev, rdreier, linux-kernel, openib-general
In-Reply-To: <20060623171457.GA3610@esmail.cup.hp.com>


[-- Attachment #1.1: Type: text/plain, Size: 1611 bytes --]

At 10:14 AM 6/23/2006, Grant Grundler wrote:
>On Fri, Jun 23, 2006 at 04:04:31PM +0200, Arjan van de Ven wrote:
> > > I thought the posted write WILL eventually get to adapter memory.  Not
> > > stall forever cached in a bridge.  I'm wrong?
> >
> > I'm not sure there is a theoretical upper bound....
>
>I'm not aware of one either since MMIO writes can travel
>across many other chips that are not constrained by
>PCI ordering rules (I'm thinking of SGI Altix...)

It is processor / coherency backplane technology specific as to the number 
of outstanding writes.  There is also no guarantee that such writes will 
hit the top of the PCI hierarchy in the order they were posted in a 
multi-core / processor system.  Hence, it is up to software to guarantee 
that ordering is preserved and to not assume anything about ordering from a 
hardware perspective.  Once a transaction hits the PCI hierarchy, then the 
PCI ordering rules apply and depending upon the transaction type and other 
rules, what is guaranteed is deterministic in nature.


> > (and if it's several msec per bridge, then you have a lot of latency
> > anyway)
>
>That's what my original concern was when I saw you point this out.
>But MMIO reads here would be expensive and many drivers tolerate
>this latency in exchange for avoiding the MMIO read in the
>performance path.

As the saying goes, MMIO Reads are "pure evil" and should be avoided at all 
costs if performance is the goal.   Even in a relatively flat I/O 
hierarchy, the additional latency is non-trivial and can lead to a 
significant loss in performance for the system.

Mike 

[-- Attachment #1.2: Type: text/html, Size: 1916 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* Re: [PATCH wireless-dev] add adm8211 driver
From: John W. Linville @ 2006-06-23 18:04 UTC (permalink / raw)
  To: Michael Wu; +Cc: netdev
In-Reply-To: <200606230951.33961.flamingice@sourmilk.net>

On Fri, Jun 23, 2006 at 09:51:24AM -0700, Michael Wu wrote:
> (resend, please merge)
> 
> This patch adds support for the ADMtek ADM8211 card. It is not completed yet, 
> but I feel the code is clean enough to merge into wireless-dev. It currently 
> supports STA mode.

I have it, I will merge it RSN...

Thanks!

John
-- 
John W. Linville
linville@tuxdriver.com

^ permalink raw reply

* [RFC IPROUTE 00/05]: Time cleanups
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi

I'm currently adding support for ktime as clocksource to the qdisc layer and
in the course of doing that cleaned up the iproute time handling a bit to
make it easier to use a different internal clock resultion and audit for
integer overflows.

Increasing iproutes internal clock resolution is necessary for all token bucket
based schedulers to take advantage of a higher kernel clock resolution because
the kernel only does lookups and additions/subtractions, which can never result
in values that have a higher precision that those passed from userspace.

The way I imagine it we have to keep the old clock sources around for userspace
compatibility since they can't deal with the higher precision values. iproute is
changed to use nsec internally and reads the kernel clock resulution from
/proc/net/psched (third value, currently assumed to always be 10^6) to convert
it's internal resolution to that of the kernel. With ktime as clocksource the
kernel will report a resulution of 10^9. The downside is that distributors can't
enable ktime unless they want to break compatibility with old iproute versions.
Any better suggestions are welcome of course.


 tc/m_estimator.c  |    4 ++--
 tc/m_police.c     |    2 +-
 tc/q_cbq.c        |   15 ++++++++-------
 tc/q_hfsc.c       |   18 +++++++++---------
 tc/q_htb.c        |    4 ++--
 tc/q_netem.c      |   12 +++---------
 tc/q_tbf.c        |   20 ++++++++++----------
 tc/tc_cbq.c       |    8 ++++----
 tc/tc_core.c      |   31 +++++++++++++++++++++++--------
 tc/tc_core.h      |    9 +++++++--
 tc/tc_estimator.c |    2 +-
 tc/tc_red.c       |    2 +-
 tc/tc_util.c      |   33 +++++++++++++++++++--------------
 tc/tc_util.h      |    7 ++++---
 14 files changed, 94 insertions(+), 73 deletions(-)

Patrick McHardy:
      [IPROUTE]: Use tc_calc_xmittime where appropriate
      [IPROUTE]: Introduce tc_calc_xmitsize and use where appropriate
      [IPROUTE]: Introduce TIME_UNITS_PER_SEC to represent internal clock resulution
      [IPROUTE]: Replace "usec" by "time" in function names
      [IPROUTE]: Add sprint_ticks() function and use in CBQ

^ permalink raw reply

* [IPROUTE 01/05]: Use tc_calc_xmittime where appropriate
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

[IPROUTE]: Use tc_calc_xmittime where appropriate

Replace expressions of the form "1000000 * size/rate" by tc_calc_xmittime().
The CBQ case deserves an extra comment: when called with bnwd=rate
tc_cbq_calc_maxidle behaves identical to tc_calc_xmittime, so use it
for clarity.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit c25b0a7730d72f266e46fee3cfd53ce9f2a15c2f
tree 39c6fbd3395e8a41170739d6cff8d3a00d31a915
parent 8f8a36487119a3cd1afe86a9649704aca088567b
author Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:02:46 +0200
committer Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:02:46 +0200

 tc/q_cbq.c   |    2 +-
 tc/q_tbf.c   |    4 ++--
 tc/tc_core.c |    2 +-
 tc/tc_red.c  |    2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/tc/q_cbq.c b/tc/q_cbq.c
index a456eda..045c377 100644
--- a/tc/q_cbq.c
+++ b/tc/q_cbq.c
@@ -147,7 +147,7 @@ static int cbq_parse_opt(struct qdisc_ut
 	if (ewma_log < 0)
 		ewma_log = TC_CBQ_DEF_EWMA;
 	lss.ewma_log = ewma_log;
-	lss.maxidle = tc_cbq_calc_maxidle(r.rate, r.rate, avpkt, lss.ewma_log, 0);
+	lss.maxidle = tc_calc_xmittime(r.rate, avpkt);
 	lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
 	lss.avpkt = avpkt;
 
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
index 6ed5e0b..87b1b29 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -245,9 +245,9 @@ static int tbf_print_opt(struct qdisc_ut
 	if (show_raw)
 		fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
 
-	latency = 1000000*(qopt->limit/(double)qopt->rate.rate) - tc_core_tick2usec(qopt->buffer);
+	latency = tc_calc_xmittime(qopt->rate.rate, qopt->limit) - tc_core_tick2usec(qopt->buffer);
 	if (qopt->peakrate.rate) {
-		double lat2 = 1000000*(qopt->limit/(double)qopt->peakrate.rate) - tc_core_tick2usec(qopt->mtu);
+		double lat2 = tc_calc_xmittime(qopt->peakrate.rate, qopt->limit) - tc_core_tick2usec(qopt->mtu);
 		if (lat2 > latency)
 			latency = lat2;
 	}
diff --git a/tc/tc_core.c b/tc/tc_core.c
index 07cf2fa..8688b63 100644
--- a/tc/tc_core.c
+++ b/tc/tc_core.c
@@ -67,7 +67,7 @@ int tc_calc_rtable(unsigned bps, __u32 *
 			sz += overhead;
 		if (sz < mpu)
 			sz = mpu;
-		rtab[i] = tc_core_usec2tick(1000000*((double)sz/bps));
+		rtab[i] = tc_calc_xmittime(bps, sz);
 	}
 	return cell_log;
 }
diff --git a/tc/tc_red.c b/tc/tc_red.c
index 385e7af..8f9bde0 100644
--- a/tc/tc_red.c
+++ b/tc/tc_red.c
@@ -71,7 +71,7 @@ int tc_red_eval_ewma(unsigned qmin, unsi
 
 int tc_red_eval_idle_damping(int Wlog, unsigned avpkt, unsigned bps, __u8 *sbuf)
 {
-	double xmit_time = tc_core_usec2tick(1000000*(double)avpkt/bps);
+	double xmit_time = tc_calc_xmittime(bps, avpkt);
 	double lW = -log(1.0 - 1.0/(1<<Wlog))/xmit_time;
 	double maxtime = 31/lW;
 	int clog;

^ permalink raw reply related

* [IPROUTE 02/05]: Introduce tc_calc_xmitsize and use where appropriate
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

[IPROUTE]: Introduce tc_calc_xmitsize and use where appropriate

Add tc_calc_xmitsize() as complement to tc_calc_xmittime(), which calculates
the size than can be transmitted at a given rate during a given time.
Replace all expressions of the form "size = rate*tc_core_tick2usec(time))/1000000"
by tc_calc_xmitsize() calls.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 06a5350c47d028448850788031625ac1961d722b
tree 23eb5f6dbbb4a864e524c048f006842733924c9e
parent c25b0a7730d72f266e46fee3cfd53ce9f2a15c2f
author Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:13:09 +0200
committer Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:13:09 +0200

 tc/m_police.c |    2 +-
 tc/q_htb.c    |    4 ++--
 tc/q_tbf.c    |    4 ++--
 tc/tc_core.c  |    5 +++++
 tc/tc_core.h  |    1 +
 5 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/tc/m_police.c b/tc/m_police.c
index 71adb59..c3d2eeb 100644
--- a/tc/m_police.c
+++ b/tc/m_police.c
@@ -334,7 +334,7 @@ #endif
 
 	fprintf(f, " police 0x%x ", p->index);
 	fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1));
-	buffer = ((double)p->rate.rate*tc_core_tick2usec(p->burst))/1000000;
+	buffer = tc_calc_xmitsize(p->rate.rate, p->burst);
 	fprintf(f, "burst %s ", sprint_size(buffer, b1));
 	fprintf(f, "mtu %s ", sprint_size(p->mtu, b1));
 	if (show_raw)
diff --git a/tc/q_htb.c b/tc/q_htb.c
index 828d4b1..9a7755f 100644
--- a/tc/q_htb.c
+++ b/tc/q_htb.c
@@ -259,9 +259,9 @@ static int htb_print_opt(struct qdisc_ut
 				fprintf(f, "quantum %d ", (int)hopt->quantum);
 		}
 	    fprintf(f, "rate %s ", sprint_rate(hopt->rate.rate, b1));
-	    buffer = ((double)hopt->rate.rate*tc_core_tick2usec(hopt->buffer))/1000000;
+	    buffer = tc_calc_xmitsize(hopt->rate.rate, hopt->buffer);
 	    fprintf(f, "ceil %s ", sprint_rate(hopt->ceil.rate, b1));
-	    cbuffer = ((double)hopt->ceil.rate*tc_core_tick2usec(hopt->cbuffer))/1000000;
+	    cbuffer = tc_calc_xmitsize(hopt->ceil.rate, hopt->cbuffer);
 	    if (show_details) {
 		fprintf(f, "burst %s/%u mpu %s overhead %s ",
 			sprint_size(buffer, b1),
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
index 87b1b29..bb13d77 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -218,7 +218,7 @@ static int tbf_print_opt(struct qdisc_ut
 	if (RTA_PAYLOAD(tb[TCA_TBF_PARMS])  < sizeof(*qopt))
 		return -1;
 	fprintf(f, "rate %s ", sprint_rate(qopt->rate.rate, b1));
-	buffer = ((double)qopt->rate.rate*tc_core_tick2usec(qopt->buffer))/1000000;
+	buffer = tc_calc_xmitsize(qopt->rate.rate, qopt->buffer);
 	if (show_details) {
 		fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1),
 			1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2));
@@ -230,7 +230,7 @@ static int tbf_print_opt(struct qdisc_ut
 	if (qopt->peakrate.rate) {
 		fprintf(f, "peakrate %s ", sprint_rate(qopt->peakrate.rate, b1));
 		if (qopt->mtu || qopt->peakrate.mpu) {
-			mtu = ((double)qopt->peakrate.rate*tc_core_tick2usec(qopt->mtu))/1000000;
+			mtu = tc_calc_xmitsize(qopt->peakrate.rate, qopt->mtu);
 			if (show_details) {
 				fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1),
 					1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2));
diff --git a/tc/tc_core.c b/tc/tc_core.c
index 8688b63..7d6dd7d 100644
--- a/tc/tc_core.c
+++ b/tc/tc_core.c
@@ -42,6 +42,11 @@ unsigned tc_calc_xmittime(unsigned rate,
 	return tc_core_usec2tick(1000000*((double)size/rate));
 }
 
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks)
+{
+	return ((double)rate*tc_core_tick2usec(ticks))/1000000;
+}
+
 /*
    rtab[pkt_len>>cell_log] = pkt_xmit_time
  */
diff --git a/tc/tc_core.h b/tc/tc_core.h
index 1537f95..d97c011 100644
--- a/tc/tc_core.h
+++ b/tc/tc_core.h
@@ -7,6 +7,7 @@ #include <linux/pkt_sched.h>
 long tc_core_usec2tick(long usec);
 long tc_core_tick2usec(long tick);
 unsigned tc_calc_xmittime(unsigned rate, unsigned size);
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks);
 int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, unsigned mpu);
 
 int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est);

^ permalink raw reply related

* [IPROUTE 03/05]: Introduce TIME_UNITS_PER_SEC to represent internal clock resulution
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

[IPROUTE]: Introduce TIME_UNITS_PER_SEC to represent internal clock resulution

Introduce TIME_UNITS_PER_SEC and conversion functions between internal
resulution and resulution expected by the kernel (currently implemented as
NOPs, only needed by HFSC, which currently always uses microseconds).

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 8e2cc14ba6fb8f940de72f307e67eb9040a5d4f7
tree da8e068525029b6571f95e7eeffcbab901f2e1be
parent 06a5350c47d028448850788031625ac1961d722b
author Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:40:34 +0200
committer Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:40:34 +0200

 tc/q_hfsc.c       |   12 ++++++------
 tc/q_tbf.c        |    4 ++--
 tc/tc_cbq.c       |    4 ++--
 tc/tc_core.c      |   14 ++++++++++++--
 tc/tc_core.h      |    4 ++++
 tc/tc_estimator.c |    2 +-
 tc/tc_util.c      |   14 +++++++-------
 7 files changed, 34 insertions(+), 20 deletions(-)

diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
index f09c606..be0587f 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -226,7 +226,7 @@ hfsc_print_sc(FILE *f, char *name, struc
 
 	fprintf(f, "%s ", name);
 	fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1));
-	fprintf(f, "d %s ", sprint_usecs(sc->d, b1));
+	fprintf(f, "d %s ", sprint_usecs(tc_core_ktime2time(sc->d), b1));
 	fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1));
 }
 
@@ -320,7 +320,7 @@ hfsc_get_sc1(int *argcp, char ***argvp, 
 		return -1;
 
 	sc->m1 = m1;
-	sc->d  = d;
+	sc->d  = tc_core_time2ktime(d);
 	sc->m2 = m2;
 
 	*argvp = argv;
@@ -367,13 +367,13 @@ hfsc_get_sc2(int *argcp, char ***argvp, 
 		return -1;
 	}
 
-	if (dmax != 0 && ceil(umax * 1000000.0 / dmax) > rate) {
+	if (dmax != 0 && ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax) > rate) {
 		/*
 		 * concave curve, slope of first segment is umax/dmax,
 		 * intersection is at dmax
 		 */
-		sc->m1 = ceil(umax * 1000000.0 / dmax); /* in bps */
-		sc->d  = dmax;
+		sc->m1 = ceil(1.0 * umax * TIME_UNITS_PER_SEC / dmax); /* in bps */
+		sc->d  = tc_core_time2ktime(dmax);
 		sc->m2 = rate;
 	} else {
 		/*
@@ -381,7 +381,7 @@ hfsc_get_sc2(int *argcp, char ***argvp, 
 		 * is at dmax - umax / rate
 		 */
 		sc->m1 = 0;
-		sc->d  = ceil(dmax - umax * 1000000.0 / rate); /* in usec */
+		sc->d  = tc_core_time2ktime(ceil(dmax - umax * TIME_UNITS_PER_SEC / rate));
 		sc->m2 = rate;
 	}
 
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
index bb13d77..f2ffb7e 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -161,9 +161,9 @@ static int tbf_parse_opt(struct qdisc_ut
 	}
 
 	if (opt.limit == 0) {
-		double lim = opt.rate.rate*(double)latency/1000000 + buffer;
+		double lim = opt.rate.rate*(double)latency/TIME_UNITS_PER_SEC + buffer;
 		if (opt.peakrate.rate) {
-			double lim2 = opt.peakrate.rate*(double)latency/1000000 + mtu;
+			double lim2 = opt.peakrate.rate*(double)latency/TIME_UNITS_PER_SEC + mtu;
 			if (lim2 < lim)
 				lim = lim2;
 		}
diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c
index 0abcc9d..c7b3a2d 100644
--- a/tc/tc_cbq.c
+++ b/tc/tc_cbq.c
@@ -38,7 +38,7 @@ unsigned tc_cbq_calc_maxidle(unsigned bn
 		if (vxmt > maxidle)
 			maxidle = vxmt;
 	}
-	return tc_core_usec2tick(maxidle*(1<<ewma_log)*1000000);
+	return tc_core_usec2tick(maxidle*(1<<ewma_log)*TIME_UNITS_PER_SEC);
 }
 
 unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt,
@@ -53,5 +53,5 @@ unsigned tc_cbq_calc_offtime(unsigned bn
 		offtime *= pow(g, -(double)minburst) - 1;
 	else
 		offtime *= 1 + (pow(g, -(double)(minburst-1)) - 1)/(1-g);
-	return tc_core_usec2tick(offtime*1000000);
+	return tc_core_usec2tick(offtime*TIME_UNITS_PER_SEC);
 }
diff --git a/tc/tc_core.c b/tc/tc_core.c
index 7d6dd7d..cf49d83 100644
--- a/tc/tc_core.c
+++ b/tc/tc_core.c
@@ -37,14 +37,24 @@ long tc_core_tick2usec(long tick)
 	return tick/tick_in_usec;
 }
 
+long tc_core_time2ktime(long time)
+{
+	return time;
+}
+
+long tc_core_ktime2time(long ktime)
+{
+	return ktime;
+}
+
 unsigned tc_calc_xmittime(unsigned rate, unsigned size)
 {
-	return tc_core_usec2tick(1000000*((double)size/rate));
+	return tc_core_usec2tick(TIME_UNITS_PER_SEC*((double)size/rate));
 }
 
 unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks)
 {
-	return ((double)rate*tc_core_tick2usec(ticks))/1000000;
+	return ((double)rate*tc_core_tick2usec(ticks))/TIME_UNITS_PER_SEC;
 }
 
 /*
diff --git a/tc/tc_core.h b/tc/tc_core.h
index d97c011..720b6d6 100644
--- a/tc/tc_core.h
+++ b/tc/tc_core.h
@@ -4,8 +4,12 @@ #define _TC_CORE_H_ 1
 #include <asm/types.h>
 #include <linux/pkt_sched.h>
 
+#define TIME_UNITS_PER_SEC		1000000
+
 long tc_core_usec2tick(long usec);
 long tc_core_tick2usec(long tick);
+long tc_core_time2ktime(long time);
+long tc_core_ktime2time(long ktime);
 unsigned tc_calc_xmittime(unsigned rate, unsigned size);
 unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks);
 int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, unsigned mpu);
diff --git a/tc/tc_estimator.c b/tc/tc_estimator.c
index 434db0f..e559add 100644
--- a/tc/tc_estimator.c
+++ b/tc/tc_estimator.c
@@ -26,7 +26,7 @@ #include "tc_core.h"
 int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est)
 {
 	for (est->interval=0; est->interval<=5; est->interval++) {
-		if (A <= (1<<est->interval)*(1000000/4))
+		if (A <= (1<<est->interval)*(TIME_UNITS_PER_SEC/4))
 			break;
 	}
 	if (est->interval > 5)
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 9cde144..2ceb631 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -221,13 +221,13 @@ int get_usecs(unsigned *usecs, const cha
 	if (*p) {
 		if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
 		    strcasecmp(p, "secs")==0)
-			t *= 1000000;
+			t *= TIME_UNITS_PER_SEC;
 		else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
 			 strcasecmp(p, "msecs") == 0)
-			t *= 1000;
+			t *= TIME_UNITS_PER_SEC/1000;
 		else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
 			 strcasecmp(p, "usecs") == 0)
-			t *= 1;
+			t *= TIME_UNITS_PER_SEC/1000000;
 		else
 			return -1;
 	}
@@ -241,10 +241,10 @@ void print_usecs(char *buf, int len, __u
 {
 	double tmp = usec;
 
-	if (tmp >= 1000000)
-		snprintf(buf, len, "%.1fs", tmp/1000000);
-	else if (tmp >= 1000)
-		snprintf(buf, len, "%.1fms", tmp/1000);
+	if (tmp >= TIME_UNITS_PER_SEC)
+		snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
+	else if (tmp >= TIME_UNITS_PER_SEC/1000)
+		snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
 	else
 		snprintf(buf, len, "%uus", usec);
 }

^ permalink raw reply related

* [IPROUTE 04/05]: Replace "usec" by "time" in function names
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

[IPROUTE]: Replace "usec" by "time" in function names

Rename functions containing "usec" since they don't necessarily return
usec units anymore.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 67edfb2c86323a1c52a60538f677310f6c75efdf
tree 37302dfd036340aac8c7f2070ebcbc570fd634ed
parent 8e2cc14ba6fb8f940de72f307e67eb9040a5d4f7
author Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:47:14 +0200
committer Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:47:14 +0200

 tc/m_estimator.c |    4 ++--
 tc/q_cbq.c       |    6 +++---
 tc/q_hfsc.c      |    6 +++---
 tc/q_netem.c     |    6 +++---
 tc/q_tbf.c       |    8 ++++----
 tc/tc_cbq.c      |    4 ++--
 tc/tc_core.c     |   10 +++++-----
 tc/tc_core.h     |    4 ++--
 tc/tc_util.c     |   14 +++++++-------
 tc/tc_util.h     |    6 +++---
 10 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/tc/m_estimator.c b/tc/m_estimator.c
index 78eda7a..898ff80 100644
--- a/tc/m_estimator.c
+++ b/tc/m_estimator.c
@@ -45,12 +45,12 @@ int parse_estimator(int *p_argc, char **
 		duparg("estimator", *argv);
 	if (matches(*argv, "help") == 0)
 		est_help();
-	if (get_usecs(&A, *argv))
+	if (get_time(&A, *argv))
 		invarg("estimator", "invalid estimator interval");
 	NEXT_ARG();
 	if (matches(*argv, "help") == 0)
 		est_help();
-	if (get_usecs(&time_const, *argv))
+	if (get_time(&time_const, *argv))
 		invarg("estimator", "invalid estimator time constant");
 	if (tc_setup_estimator(A, time_const, est) < 0) {
 		fprintf(stderr, "Error: estimator parameters are out of range.\n");
diff --git a/tc/q_cbq.c b/tc/q_cbq.c
index 045c377..648acd0 100644
--- a/tc/q_cbq.c
+++ b/tc/q_cbq.c
@@ -500,17 +500,17 @@ static int cbq_print_opt(struct qdisc_ut
 	if (lss && show_details) {
 		fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
 		if (lss->maxidle) {
-			fprintf(f, "maxidle %luus ", tc_core_tick2usec(lss->maxidle>>lss->ewma_log));
+			fprintf(f, "maxidle %luus ", tc_core_tick2time(lss->maxidle>>lss->ewma_log));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->maxidle);
 		}
 		if (lss->minidle!=0x7fffffff) {
-			fprintf(f, "minidle %luus ", tc_core_tick2usec(lss->minidle>>lss->ewma_log));
+			fprintf(f, "minidle %luus ", tc_core_tick2time(lss->minidle>>lss->ewma_log));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->minidle);
 		}
 		if (lss->offtime) {
-			fprintf(f, "offtime %luus ", tc_core_tick2usec(lss->offtime));
+			fprintf(f, "offtime %luus ", tc_core_tick2time(lss->offtime));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->offtime);
 		}
diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c
index be0587f..6c160c9 100644
--- a/tc/q_hfsc.c
+++ b/tc/q_hfsc.c
@@ -226,7 +226,7 @@ hfsc_print_sc(FILE *f, char *name, struc
 
 	fprintf(f, "%s ", name);
 	fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1));
-	fprintf(f, "d %s ", sprint_usecs(tc_core_ktime2time(sc->d), b1));
+	fprintf(f, "d %s ", sprint_time(tc_core_ktime2time(sc->d), b1));
 	fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1));
 }
 
@@ -303,7 +303,7 @@ hfsc_get_sc1(int *argcp, char ***argvp, 
 
 	if (matches(*argv, "d") == 0) {
 		NEXT_ARG();
-		if (get_usecs(&d, *argv) < 0) {
+		if (get_time(&d, *argv) < 0) {
 			explain1("d");
 			return -1;
 		}
@@ -346,7 +346,7 @@ hfsc_get_sc2(int *argcp, char ***argvp, 
 
 	if (matches(*argv, "dmax") == 0) {
 		NEXT_ARG();
-		if (get_usecs(&dmax, *argv) < 0) {
+		if (get_time(&dmax, *argv) < 0) {
 			explain1("dmax");
 			return -1;
 		}
diff --git a/tc/q_netem.c b/tc/q_netem.c
index 757edca..cc16a28 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -108,16 +108,16 @@ static int get_ticks(__u32 *ticks, const
 {
 	unsigned t;
 
-	if(get_usecs(&t, str))
+	if(get_time(&t, str))
 		return -1;
 	
-	*ticks = tc_core_usec2tick(t);
+	*ticks = tc_core_time2tick(t);
 	return 0;
 }
 
 static char *sprint_ticks(__u32 ticks, char *buf)
 {
-	return sprint_usecs(tc_core_tick2usec(ticks), buf);
+	return sprint_time(tc_core_tick2time(ticks), buf);
 }
 
 
diff --git a/tc/q_tbf.c b/tc/q_tbf.c
index f2ffb7e..4db4b44 100644
--- a/tc/q_tbf.c
+++ b/tc/q_tbf.c
@@ -67,7 +67,7 @@ static int tbf_parse_opt(struct qdisc_ut
 				fprintf(stderr, "Double \"limit/latency\" spec\n");
 				return -1;
 			}
-			if (get_usecs(&latency, *argv)) {
+			if (get_time(&latency, *argv)) {
 				explain1("latency");
 				return -1;
 			}
@@ -245,13 +245,13 @@ static int tbf_print_opt(struct qdisc_ut
 	if (show_raw)
 		fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
 
-	latency = tc_calc_xmittime(qopt->rate.rate, qopt->limit) - tc_core_tick2usec(qopt->buffer);
+	latency = tc_calc_xmittime(qopt->rate.rate, qopt->limit) - tc_core_tick2time(qopt->buffer);
 	if (qopt->peakrate.rate) {
-		double lat2 = tc_calc_xmittime(qopt->peakrate.rate, qopt->limit) - tc_core_tick2usec(qopt->mtu);
+		double lat2 = tc_calc_xmittime(qopt->peakrate.rate, qopt->limit) - tc_core_tick2time(qopt->mtu);
 		if (lat2 > latency)
 			latency = lat2;
 	}
-	fprintf(f, "lat %s ", sprint_usecs(tc_core_tick2usec(latency), b1));
+	fprintf(f, "lat %s ", sprint_time(tc_core_tick2time(latency), b1));
 
 	return 0;
 }
diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c
index c7b3a2d..0bb262e 100644
--- a/tc/tc_cbq.c
+++ b/tc/tc_cbq.c
@@ -38,7 +38,7 @@ unsigned tc_cbq_calc_maxidle(unsigned bn
 		if (vxmt > maxidle)
 			maxidle = vxmt;
 	}
-	return tc_core_usec2tick(maxidle*(1<<ewma_log)*TIME_UNITS_PER_SEC);
+	return tc_core_time2tick(maxidle*(1<<ewma_log)*TIME_UNITS_PER_SEC);
 }
 
 unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt,
@@ -53,5 +53,5 @@ unsigned tc_cbq_calc_offtime(unsigned bn
 		offtime *= pow(g, -(double)minburst) - 1;
 	else
 		offtime *= 1 + (pow(g, -(double)(minburst-1)) - 1)/(1-g);
-	return tc_core_usec2tick(offtime*TIME_UNITS_PER_SEC);
+	return tc_core_time2tick(offtime*TIME_UNITS_PER_SEC);
 }
diff --git a/tc/tc_core.c b/tc/tc_core.c
index cf49d83..063995c 100644
--- a/tc/tc_core.c
+++ b/tc/tc_core.c
@@ -27,12 +27,12 @@ static __u32 t2us=1;
 static __u32 us2t=1;
 static double tick_in_usec = 1;
 
-long tc_core_usec2tick(long usec)
+long tc_core_time2tick(long time)
 {
-	return usec*tick_in_usec;
+	return time*tick_in_usec;
 }
 
-long tc_core_tick2usec(long tick)
+long tc_core_tick2time(long tick)
 {
 	return tick/tick_in_usec;
 }
@@ -49,12 +49,12 @@ long tc_core_ktime2time(long ktime)
 
 unsigned tc_calc_xmittime(unsigned rate, unsigned size)
 {
-	return tc_core_usec2tick(TIME_UNITS_PER_SEC*((double)size/rate));
+	return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/rate));
 }
 
 unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks)
 {
-	return ((double)rate*tc_core_tick2usec(ticks))/TIME_UNITS_PER_SEC;
+	return ((double)rate*tc_core_tick2time(ticks))/TIME_UNITS_PER_SEC;
 }
 
 /*
diff --git a/tc/tc_core.h b/tc/tc_core.h
index 720b6d6..52954be 100644
--- a/tc/tc_core.h
+++ b/tc/tc_core.h
@@ -6,8 +6,8 @@ #include <linux/pkt_sched.h>
 
 #define TIME_UNITS_PER_SEC		1000000
 
-long tc_core_usec2tick(long usec);
-long tc_core_tick2usec(long tick);
+long tc_core_time2tick(long time);
+long tc_core_tick2time(long tick);
 long tc_core_time2ktime(long time);
 long tc_core_ktime2time(long ktime);
 unsigned tc_calc_xmittime(unsigned rate, unsigned size);
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 2ceb631..321bac0 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -209,7 +209,7 @@ char * sprint_rate(__u32 rate, char *buf
 	return buf;
 }
 
-int get_usecs(unsigned *usecs, const char *str)
+int get_time(unsigned *time, const char *str)
 {
 	double t;
 	char *p;
@@ -232,26 +232,26 @@ int get_usecs(unsigned *usecs, const cha
 			return -1;
 	}
 
-	*usecs = t;
+	*time = t;
 	return 0;
 }
 
 
-void print_usecs(char *buf, int len, __u32 usec)
+void print_time(char *buf, int len, __u32 time)
 {
-	double tmp = usec;
+	double tmp = time;
 
 	if (tmp >= TIME_UNITS_PER_SEC)
 		snprintf(buf, len, "%.1fs", tmp/TIME_UNITS_PER_SEC);
 	else if (tmp >= TIME_UNITS_PER_SEC/1000)
 		snprintf(buf, len, "%.1fms", tmp/(TIME_UNITS_PER_SEC/1000));
 	else
-		snprintf(buf, len, "%uus", usec);
+		snprintf(buf, len, "%uus", time);
 }
 
-char * sprint_usecs(__u32 usecs, char *buf)
+char * sprint_time(__u32 time, char *buf)
 {
-	print_usecs(buf, SPRINT_BSIZE-1, usecs);
+	print_time(buf, SPRINT_BSIZE-1, time);
 	return buf;
 }
 
diff --git a/tc/tc_util.h b/tc/tc_util.h
index 1aa1bda..aba4adb 100644
--- a/tc/tc_util.h
+++ b/tc/tc_util.h
@@ -46,17 +46,17 @@ extern int get_rate(unsigned *rate, cons
 extern int get_percent(unsigned *percent, const char *str);
 extern int get_size(unsigned *size, const char *str);
 extern int get_size_and_cell(unsigned *size, int *cell_log, char *str);
-extern int get_usecs(unsigned *usecs, const char *str);
+extern int get_time(unsigned *time, const char *str);
 extern void print_rate(char *buf, int len, __u32 rate);
 extern void print_size(char *buf, int len, __u32 size);
 extern void print_percent(char *buf, int len, __u32 percent);
 extern void print_qdisc_handle(char *buf, int len, __u32 h);
-extern void print_usecs(char *buf, int len, __u32 usecs);
+extern void print_time(char *buf, int len, __u32 time);
 extern char * sprint_rate(__u32 rate, char *buf);
 extern char * sprint_size(__u32 size, char *buf);
 extern char * sprint_qdisc_handle(__u32 h, char *buf);
 extern char * sprint_tc_classid(__u32 h, char *buf);
-extern char * sprint_usecs(__u32 usecs, char *buf);
+extern char * sprint_time(__u32 time, char *buf);
 extern char * sprint_percent(__u32 percent, char *buf);
 
 extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);

^ permalink raw reply related

* [IPROUTE 05/05]: Add sprint_ticks() function and use in CBQ
From: Patrick McHardy @ 2006-06-23 18:06 UTC (permalink / raw)
  To: hemminger; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

[IPROUTE]: Add sprint_ticks() function and use in CBQ

Add helper function to print ticks to avoid assumptions about clock
resolution in CBQ.

Signed-off-by: Patrick McHardy <kaber@trash.net>

---
commit 182666ac89f3089a1771564620998fb4f315bd87
tree ce7c3109b7e8ff9a4665e175490a62f8c300ea33
parent 67edfb2c86323a1c52a60538f677310f6c75efdf
author Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:52:11 +0200
committer Patrick McHardy <kaber@trash.net> Fri, 23 Jun 2006 19:52:11 +0200

 tc/q_cbq.c   |    7 ++++---
 tc/q_netem.c |    6 ------
 tc/tc_util.c |    5 +++++
 tc/tc_util.h |    1 +
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/tc/q_cbq.c b/tc/q_cbq.c
index 648acd0..abf0468 100644
--- a/tc/q_cbq.c
+++ b/tc/q_cbq.c
@@ -418,6 +418,7 @@ static int cbq_print_opt(struct qdisc_ut
 	struct tc_cbq_wrropt *wrr = NULL;
 	struct tc_cbq_fopt *fopt = NULL;
 	struct tc_cbq_ovl *ovl = NULL;
+	SPRINT_BUF(b1);
 
 	if (opt == NULL)
 		return 0;
@@ -500,17 +501,17 @@ static int cbq_print_opt(struct qdisc_ut
 	if (lss && show_details) {
 		fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
 		if (lss->maxidle) {
-			fprintf(f, "maxidle %luus ", tc_core_tick2time(lss->maxidle>>lss->ewma_log));
+			fprintf(f, "maxidle %s ", sprint_ticks(lss->maxidle>>lss->ewma_log, b1));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->maxidle);
 		}
 		if (lss->minidle!=0x7fffffff) {
-			fprintf(f, "minidle %luus ", tc_core_tick2time(lss->minidle>>lss->ewma_log));
+			fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->minidle);
 		}
 		if (lss->offtime) {
-			fprintf(f, "offtime %luus ", tc_core_tick2time(lss->offtime));
+			fprintf(f, "offtime %s ", sprint_ticks(lss->offtime, b1));
 			if (show_raw)
 				fprintf(f, "[%08x] ", lss->offtime);
 		}
diff --git a/tc/q_netem.c b/tc/q_netem.c
index cc16a28..0d7f4d5 100644
--- a/tc/q_netem.c
+++ b/tc/q_netem.c
@@ -115,12 +115,6 @@ static int get_ticks(__u32 *ticks, const
 	return 0;
 }
 
-static char *sprint_ticks(__u32 ticks, char *buf)
-{
-	return sprint_time(tc_core_tick2time(ticks), buf);
-}
-
-
 static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, 
 			   struct nlmsghdr *n)
 {
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 321bac0..c1e02f4 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -255,6 +255,11 @@ char * sprint_time(__u32 time, char *buf
 	return buf;
 }
 
+char * sprint_ticks(__u32 ticks, char *buf)
+{
+	return sprint_time(tc_core_tick2time(ticks), buf);
+}
+
 int get_size(unsigned *size, const char *str)
 {
 	double sz;
diff --git a/tc/tc_util.h b/tc/tc_util.h
index aba4adb..17e7b99 100644
--- a/tc/tc_util.h
+++ b/tc/tc_util.h
@@ -57,6 +57,7 @@ extern char * sprint_size(__u32 size, ch
 extern char * sprint_qdisc_handle(__u32 h, char *buf);
 extern char * sprint_tc_classid(__u32 h, char *buf);
 extern char * sprint_time(__u32 time, char *buf);
+extern char * sprint_ticks(__u32 ticks, char *buf);
 extern char * sprint_percent(__u32 percent, char *buf);
 
 extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats);

^ permalink raw reply related

* Re: [PATCH]: e1000: Janitor: Use #defined values for literals
From: Auke Kok @ 2006-06-23 18:07 UTC (permalink / raw)
  To: Linas Vepstas
  Cc: netdev, john.ronciak, jesse.brandeburg, jeffrey.t.kirsher,
	Zhang, Yanmin, Jeff Garzik, linux-kernel
In-Reply-To: <20060623163624.GM8866@austin.ibm.com>

Linas Vepstas wrote:
> Minor janitorial patch: use #defines for literal values.
> 
> Signed-off-by: Linas Vepstas <linas@austin.ibm.com>

Ack! I thought we had gotten these out already.

Cheers,

Auke



> 
> ----
>  drivers/net/e1000/e1000_main.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> Index: linux-2.6.17-rc6-mm2/drivers/net/e1000/e1000_main.c
> ===================================================================
> --- linux-2.6.17-rc6-mm2.orig/drivers/net/e1000/e1000_main.c	2006-06-13 18:13:30.000000000 -0500
> +++ linux-2.6.17-rc6-mm2/drivers/net/e1000/e1000_main.c	2006-06-23 11:27:47.000000000 -0500
> @@ -4663,8 +4663,8 @@ static pci_ers_result_t e1000_io_slot_re
>  	}
>  	pci_set_master(pdev);
>  
> -	pci_enable_wake(pdev, 3, 0);
> -	pci_enable_wake(pdev, 4, 0); /* 4 == D3 cold */
> +	pci_enable_wake(pdev, PCI_D3hot, 0);
> +	pci_enable_wake(pdev, PCI_D3cold, 0);
>  
>  	/* Perform card reset only on one instance of the card */
>  	if (PCI_FUNC (pdev->devfn) != 0)
> -
> 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: [RFC IPROUTE 00/05]: Time cleanups
From: Patrick McHardy @ 2006-06-23 18:09 UTC (permalink / raw)
  To: shemminger; +Cc: netdev, hadi
In-Reply-To: <20060623180642.13183.74864.sendpatchset@localhost.localdomain>

D'oh, got Stephen's address wrong. Please fix it (add 's' in front) in
replies to this thread.

Patrick McHardy wrote:
> I'm currently adding support for ktime as clocksource to the qdisc layer and
> in the course of doing that cleaned up the iproute time handling a bit to
> make it easier to use a different internal clock resultion and audit for
> integer overflows.
> 
> Increasing iproutes internal clock resolution is necessary for all token bucket
> based schedulers to take advantage of a higher kernel clock resolution because
> the kernel only does lookups and additions/subtractions, which can never result
> in values that have a higher precision that those passed from userspace.
> 
> The way I imagine it we have to keep the old clock sources around for userspace
> compatibility since they can't deal with the higher precision values. iproute is
> changed to use nsec internally and reads the kernel clock resulution from
> /proc/net/psched (third value, currently assumed to always be 10^6) to convert
> it's internal resolution to that of the kernel. With ktime as clocksource the
> kernel will report a resulution of 10^9. The downside is that distributors can't
> enable ktime unless they want to break compatibility with old iproute versions.
> Any better suggestions are welcome of course.
> 
> 
>  tc/m_estimator.c  |    4 ++--
>  tc/m_police.c     |    2 +-
>  tc/q_cbq.c        |   15 ++++++++-------
>  tc/q_hfsc.c       |   18 +++++++++---------
>  tc/q_htb.c        |    4 ++--
>  tc/q_netem.c      |   12 +++---------
>  tc/q_tbf.c        |   20 ++++++++++----------
>  tc/tc_cbq.c       |    8 ++++----
>  tc/tc_core.c      |   31 +++++++++++++++++++++++--------
>  tc/tc_core.h      |    9 +++++++--
>  tc/tc_estimator.c |    2 +-
>  tc/tc_red.c       |    2 +-
>  tc/tc_util.c      |   33 +++++++++++++++++++--------------
>  tc/tc_util.h      |    7 ++++---
>  14 files changed, 94 insertions(+), 73 deletions(-)
> 
> Patrick McHardy:
>       [IPROUTE]: Use tc_calc_xmittime where appropriate
>       [IPROUTE]: Introduce tc_calc_xmitsize and use where appropriate
>       [IPROUTE]: Introduce TIME_UNITS_PER_SEC to represent internal clock resulution
>       [IPROUTE]: Replace "usec" by "time" in function names
>       [IPROUTE]: Add sprint_ticks() function and use in CBQ

^ permalink raw reply


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