summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/digiPiper/digiPs.c
blob: c6ae6b0b656a62bdb46f7d9b090d700404d173ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
/*
 * digiPs.c
 *
 * Copyright (C) 2009 by Digi International Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */

/*
 * This file contains the routines that are related to transmitting
 * frames.
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kthread.h>

#include "pipermain.h"
#include "mac.h"
#include "phy.h"
#include "airoha.h"
#include "digiPs.h"

/*
 * Macro converts milliseconds to HZ.
 *
 * TODO:  Look for standard Linux version of this.
 */
#define MILLS_TO_JIFFIES(x)		((((x)*HZ) + 500) / 1000)




#define MINIMUM_SLEEP_PERIOD	(20)

/*
 * Amount of time before shutdown deadline to start the shutdown process.  This
 * should allow enough time to get one last frame out, which will be the
 * null-data frame notifying the AP that we are shutting down.
 */
#define PS_TRANSMITTER_SHUTDOWN_MS 	(10)

/*
 * Amount of time we will wake up before the next beacon.  We try to wake up before
 * the next beacon so that we don't miss it.
 */
#define PS_WAKE_BEFORE_BEACON_MS	(20)

/*
 * Minimum amount of time we we keep awake for.
 */
#define PS_MINUMUM_POWER_UP_PERIOD_MS	(10)

/*
 * Minimum amount of time we will sleep.  If we will end up sleeping
 * for less than this, then don't go to sleep.
 */
#define PS_MINIMUM_SLEEP_TIME (10)

/*
 * Length of one tick of the transmit clean up timer in ms.  This is
 * the minimum amount of time we will sleep for.
 */
#define PS_TRANSMIT_TIMER_TICK_MS	((1000/HZ) ? (1000/HZ) : 1)

/*
 * Length of time we will wait past the expected arrival of a beacon before assuming
 * that we missed it.
 */
#define PS_BEACON_TIMEOUT_MS		(40)


/*
 * Minimum beacon interval we will support for duty cycling.  There is so much overhead
 * in duty cycling that it doesn't make sense to do it for short beacon intervals.
 */
#define PS_MINIMUM_BEACON_INT		(100)

/*
 * Amount of time we will pause duty cycling for after receiving an event that suggests
 * wpa_supplicant is attempting to reassociate with an AP.
 */
#define PS_SCAN_DELAY				(5000)


// Powersave register index
#define	INDX_GEN_CONTROL    0	// General control
#define	INDX_GEN_STATUS	    1	// General status
#define	INDX_RSSI_AES		2	// RSSI and AES status
#define	INDX_INTR_MASK	    3	// Interrupt mask
#define INDX_SPI_CTRL       4	// RF SPI control
#define INDX_CONF1          5	// Configuration 1
#define INDX_CONF2          6	// Configuration 2
#define	INDX_AES_MODE		7	// ARS mode
#define INDX_OUT_CTRL       8	// Output control
#define INDX_MAC_CONTROL    9	// MAC control
#define INDX_STAID_1		10  // first part of stations ID
#define INDX_STAID_2		11  // 2nd part of station ID
#define INDX_BSSID_1		12  // 1st part of BSS ID
#define INDX_BSSID_2		13  // 2nd part of BSS ID
#define INDX_BRS_SSID		14  // BRS mask and SSID length
#define INDX_BACKOFF		15	// backoff
#define INDX_DTIM_LISTEN	16  // DTIM perido and listen interval
#define INDX_BEACON_INT		17	// beacon interval
#define INDX_MAC_CTL		18	// MAC control register
#define INDX_BEACON_MASK	19	// beacon mask and backoff
#define INDX_TOTAL          20

static u32 savedRegs[INDX_TOTAL];	// Used to save registers for sleep mode


/*
 * TODO: Delete this.
 */
struct ps_stats {
	unsigned int modeStart;
	unsigned int cycleStart;
	unsigned int receivedBeacons;
	unsigned int missedBeacons;
	unsigned int jiffiesOn;
	unsigned int jiffiesOff;
} stats;



static void ps_free_frame(struct ieee80211_hw *hw, struct sk_buff *skb)
{
	struct piper_priv *piperp = hw->priv;

	if (skb) {
		dev_kfree_skb(skb);
		piperp->ps.frames_pending--;
	}
}





#define ACK_SIZE		(14)	/* length of ACK in bytes */

// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
// not including SIFS and PLCP preamble/header
#define	CCK_DURATION(bytes, rate)		((16*(bytes)+(rate)-1)/(rate))

#define USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble)	((!is_1_Mbit) & use_short_preamble)

// Length (in usecs) of SIFS and PLCP preamble/header.
#define	PRE_LEN(is_1_Mbit, use_short_preamble)			(USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ? 106 : 202)

// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
// including SIFS and PLCP preamble/header
#define	OFDM_DURATION(bytes, rate)	(36 + 4*((4*(bytes)+(rate)+10)/(rate)))

static unsigned int getRateIndex(struct piper_priv *piperp)
{
	unsigned int rates = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
	unsigned int result = AIROHA_LOWEST_OFDM_RATE_INDEX;

	if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_2GHZ) {
		if ((rates & MAC_PSK_BRS_MASK) != 0) {
			result = AIROHA_LOWEST_PSK_RATE_INDEX;
		}
	}

	return result;
}

static int getAckDuration(struct piper_priv *piperp)
{
	bool is_1_Mbit = (getRateIndex(piperp) == AIROHA_LOWEST_PSK_RATE_INDEX);
	int duration = 0;

	if (is_1_Mbit) {
		duration = CCK_DURATION(ACK_SIZE, 1);
	} else {
		duration = OFDM_DURATION(ACK_SIZE, 6);
	}

	duration += PRE_LEN(is_1_Mbit, piperp->use_short_preamble);

	return duration;
}


/*
 * This function is used to notify the AP about the current state of
 * power save.  One of the bits in the 802.11 header field is set to
 * indicate the status of power save.  This bit is actually set appropriately
 * for all frames sent, we just send a null data frame just to make
 * sure something is sent to the AP in a reasonable amount of time.
 */
/*
 * Possible values for is_power_management_on argument
 */
#define POWERING_DOWN		(true)
#define POWERED_UP			(false)
void piper_sendNullDataFrame(struct piper_priv *piperp, bool is_power_management_on)
{
	struct sk_buff *skb = NULL;
	_80211HeaderType *header;
	struct ieee80211_tx_info *tx_info;

	skb =
	    __dev_alloc_skb(sizeof(_80211HeaderType) +
			    piperp->hw->extra_tx_headroom, GFP_ATOMIC);
	if (skb == NULL)
		goto piper_sendNullDataFrame_Exit;

	tx_info = (struct ieee80211_tx_info *) skb->cb;

	skb_reserve(skb, piperp->hw->extra_tx_headroom);
	header = (_80211HeaderType *) skb_put(skb, sizeof(_80211HeaderType));
	memset(header, 0, sizeof(*header));
	header->fc.type = TYPE_NULL_DATA;
	header->fc.pwrMgt = is_power_management_on;
	header->duration = getAckDuration(piperp);
	memcpy(header->addr1, piperp->bssid, sizeof(header->addr1));
	memcpy(header->addr2, piperp->pdata->macaddr, sizeof(header->addr2));
	memcpy(header->addr3, piperp->bssid, sizeof(header->addr3));

	tx_info->flags = IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT;
	tx_info->band = piperp->rf->getBand(piperp->channel);
	tx_info->antenna_sel_tx = 1;	/* actually this is ignored for now */
	tx_info->control.rates[0].idx = 0;
	tx_info->control.rates[0].count = 2;	/* no retries.  Don't tie us up waiting for an ACK */
	tx_info->control.rates[0].flags = 0;
	tx_info->control.rates[1].idx = -1;
	tx_info->control.rts_cts_rate_idx = -1;
	piperp->ps.frames_pending++;

	if (piper_hw_tx_private(piperp->hw, skb, ps_free_frame) != 0) {
		/* printk(KERN_ERR
		       "piper_hw_tx() failed unexpectedly when sending null data frame.\n"); */
		ps_free_frame(piperp->hw, skb);
	}

piper_sendNullDataFrame_Exit:
	return;
}




#define RESET_PIPER		(1)				/* must be set to 1 (0 case is only for debugging)*/
#define PS_DONT_FORCE		(false)		/* set force to this value if we want to be safe */
#define PS_FORCE_POWER_DOWN	(true)		/* set force to this value to shut down the H/W reguardless*/
/*
 * This routine shuts down Piper and the Airoha transceiver.  First we check to
 * make sure the driver and H/W are idle.  Then we save the state of the H/W.
 * Then we shut down the Airoha and place piper into reset.
 */
int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force)
{
	/*
	 * Interrupts are already disabled when we get here.
	 */

	if (piperp->ps.poweredDown)
		return 0;

	savedRegs[INDX_INTR_MASK] = piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
	piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);

    if (!force) {
    	if (   (piperp->ps.rxTaskletRunning)
    	    || ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY))
    		|| (  (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)
    	     	& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0)
			|| (piperp->tx_tasklet_running)
    		|| (  (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
    			& BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
    		|| (piperp->ac->rd_reg(piperp, BB_IRQ_STAT) & savedRegs[INDX_INTR_MASK])) {

			piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
    		return -1;
		}
    }

	disable_irq(piperp->irq);

	savedRegs[INDX_GEN_CONTROL] = piperp->ac->rd_reg(piperp, BB_GENERAL_CTL);
	savedRegs[INDX_GEN_STATUS] = piperp->ac->rd_reg(piperp, BB_GENERAL_STAT);
	savedRegs[INDX_RSSI_AES] = piperp->ac->rd_reg(piperp, BB_RSSI) & ~BB_RSSI_EAS_BUSY;
	savedRegs[INDX_SPI_CTRL] = piperp->ac->rd_reg(piperp, BB_SPI_CTRL);
	savedRegs[INDX_CONF1] = piperp->ac->rd_reg(piperp, BB_TRACK_CONTROL);
	savedRegs[INDX_CONF2] = piperp->ac->rd_reg(piperp, BB_CONF_2);
	savedRegs[INDX_OUT_CTRL] = piperp->ac->rd_reg(piperp, BB_OUTPUT_CONTROL);
	savedRegs[INDX_MAC_CONTROL] = piperp->ac->rd_reg(piperp, MAC_CTL);

	savedRegs[INDX_STAID_1]		= piperp->ac->rd_reg(piperp, MAC_STA_ID0);
	savedRegs[INDX_STAID_2]		= piperp->ac->rd_reg(piperp, MAC_STA_ID1);
	savedRegs[INDX_BSSID_1]		= piperp->ac->rd_reg(piperp, MAC_BSS_ID0);
	savedRegs[INDX_BSSID_2]		= piperp->ac->rd_reg(piperp, MAC_BSS_ID1);
	savedRegs[INDX_BRS_SSID]	= piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
	savedRegs[INDX_BACKOFF]		= piperp->ac->rd_reg(piperp, MAC_BACKOFF);
	savedRegs[INDX_DTIM_LISTEN]	= piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD);
	savedRegs[INDX_BEACON_INT]	= piperp->ac->rd_reg(piperp, MAC_CFP_ATIM);
	savedRegs[INDX_MAC_CTL]		= piperp->ac->rd_reg(piperp, MAC_CTL);
	savedRegs[INDX_BEACON_MASK]	= piperp->ac->rd_reg(piperp, MAC_BEACON_FILT);

	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);	//disable receiving
	piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
	piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);

#if RESET_PIPER
    // Power down the airoha transceiver
	piperp->rf->power_on(piperp->hw, false);
	udelay(10);
	// hold the transceiver in reset mode
	if (piperp->pdata->reset)
		piperp->pdata->reset(piperp, 1);
#endif
	stats.jiffiesOn += jiffies - stats.cycleStart;
	stats.cycleStart = jiffies;
	piperp->ps.poweredDown = true;

	return 0;
}


#define PS_NO_SPIKE_SUPPRESSION		(false)		/* want_spike_suppression = don't want spike suppression*/
#define PS_WANT_SPIKE_SUPPRESSION	(true)		/* want_spike_suppression = do spike suppression*/
/*
 * Turn the H/W back on after it was shutdown with piper_MacEnterSleepMode.
 *
 *	1. Power up the hardware.
 *  2. Perform the spike suppression routine if desired.  The spike suppression
 *     routine resyncs the clocks in Piper in order to prevent us from generating
 *     noise spikes at 1 and 2 MHz.  Unfortunately it takes an indeterminate amount
 *     of time, so we normally don't do it and just make sure we do not send at
 *	   at those data rates while duty cycling.
 * 3.  Set the channel.  The Airoha was shut off so we have to set the channel
 *     again.
 * 4.  Restore the state of piper registers.
 * 5.  Zero out and reset the transmitter FIFO.  Mike Schaffner claims this should
 *     not be necessary, but we seem to run into trouble when we don't do it.
 * 6.  Restore the interrupts.
 */
void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression)
{
	int i;
// #define WANT_DEBUG
#ifdef WANT_DEBUG
	static unsigned int run = 0;
#endif

#if RESET_PIPER

	if (piperp->pdata->reset) {
#ifdef WANT_DEBUG
		if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
			printk(KERN_ERR "**** While in reset, run = %d\n", run);
			digiWifiDumpRegisters(piperp, MAIN_REGS);
			while(1);
		}
		if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
			printk(KERN_ERR "**** While in reset AES busy set, run = %d\n", run);
			digiWifiDumpRegisters(piperp, MAIN_REGS);
			while(1);
		}
#endif
	    piperp->pdata->reset(piperp, 0);
	    udelay(10);

#ifdef WANT_DEBUG
		if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
			printk(KERN_ERR "**** After reset, run = %d\n", run);
			digiWifiDumpRegisters(piperp, MAIN_REGS);
			while(1);
		}
		if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
			printk(KERN_ERR "**** After reset AES busy set, run = %d\n", run);
			digiWifiDumpRegisters(piperp, MAIN_REGS);
			while(1);
		}
		run++;
#endif
	    piperp->rf->power_on(piperp->hw, true);
	    mdelay(1);
	    piper_spike_suppression(piperp, want_spike_suppression);
	}
#endif

	piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);


#if RESET_PIPER
	piperp->rf->set_chan_no_rx(piperp->hw, piperp->channel);
#endif

      // store the registers back

	piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x30000000, op_write);
	piperp->ac->wr_reg(piperp, BB_RSSI, savedRegs[INDX_RSSI_AES] & ~BB_RSSI_EAS_BUSY, op_write);

//	piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
	piperp->ac->wr_reg(piperp, BB_SPI_CTRL, savedRegs[INDX_SPI_CTRL], op_write);
	piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, savedRegs[INDX_CONF1], op_write);
	piperp->ac->wr_reg(piperp, BB_CONF_2, savedRegs[INDX_CONF2], op_write);
	piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
	piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, savedRegs[INDX_OUT_CTRL], op_write);
	piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CONTROL], op_write);

	piperp->ac->wr_reg(piperp, MAC_STA_ID0,     savedRegs[INDX_STAID_1],          op_write);
	piperp->ac->wr_reg(piperp, MAC_STA_ID1,     savedRegs[INDX_STAID_2],          op_write);
	piperp->ac->wr_reg(piperp, MAC_BSS_ID0,     savedRegs[INDX_BSSID_1],          op_write);
	piperp->ac->wr_reg(piperp, MAC_BSS_ID1,     savedRegs[INDX_BSSID_2],          op_write);
	piperp->ac->wr_reg(piperp, MAC_SSID_LEN,    savedRegs[INDX_BRS_SSID],       op_write);
	piperp->ac->wr_reg(piperp, MAC_BACKOFF,     savedRegs[INDX_BACKOFF],          op_write);
	piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, savedRegs[INDX_DTIM_LISTEN],  op_write);
	piperp->ac->wr_reg(piperp, MAC_CFP_ATIM,    savedRegs[INDX_BEACON_INT], op_write);
	piperp->ac->wr_reg(piperp, MAC_CTL,         savedRegs[INDX_MAC_CTL],          op_write);
	piperp->ac->wr_reg(piperp, MAC_BEACON_FILT, savedRegs[INDX_BEACON_MASK],  op_write);

//****
	// set bit-11 in the general control register to a 1 to start the processors
	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
			   BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);

	// set the TX-hold bit
	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);

	// clear the TX-FIFO memory
	for (i = 0; i < 448; i++)
		piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);

	// clear RX-FIFO memory
	for (i = 0; i < 512; i++)
		piperp->ac->rd_reg(piperp, BB_DATA_FIFO);

	// reset the TX-FIFO and RX-FIFO
	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200E0, op_write);


	// release the TX-hold and reset
	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);


//***
	/*
	 * Reset the interrupt mask.  We could have been receiving a frame when we
	 * powered down.  This could cause us to store the wrong mask, so we want
	 * to make sure we enabe the RX interrupts.  However, we should not have the
	 * TX interrupts enabled when we come out of power save mode.
	 */
	udelay(50);
	piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
	piperp->clear_irq_mask_bit(piperp, 0xffffffff);
	piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
	piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
	             (savedRegs[INDX_GEN_CONTROL] | 0x37000000 |
	              BB_GENERAL_CTL_RX_EN), op_write);

	stats.jiffiesOff += jiffies - stats.cycleStart;
	stats.cycleStart = jiffies;

	/* TODO, this is a temporary workaround and should be better analyzed in future.
	 * The problem is that the general power save code is not synchronized with the
	 * dynamic PW and this is causing that, the following line, unbalances the
	 * piper wireless irq */
	if (piperp->ps.poweredDown != false)
		enable_irq(piperp->irq);

	piperp->ps.poweredDown = false;
}




/*
 * So what crazy thing are we doing here?  Well, Piper has a bug where it
 * can send noise spikes at 1 Mbps and 2 Mbps if it is powered up without
 * running a special spike suppression routine.  The spike suppression code
 * takes an average of 30 ms, and I have timed it taking as long as 300 ms.
 * This is not something you want to use for duty cycling.  The solution is
 * to avoid sending at those two rates.  After the transmit routine determines
 * the rate mac80211 specified, it will call us and we will decide whether
 * we like that rate.  If it is one of our bad rates, then we will bump it
 * up to a good rate.
 */
struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
					   struct ieee80211_rate *rate)
{
#define BAD_RATE_1MBPS		(10)
#define BAD_RATE_2MBPS		(20)
	if ((piperp->ps.mode == PS_MODE_LOW_POWER) && (rate != NULL)) {
		if ((rate->bitrate == BAD_RATE_1MBPS)
		    || (rate->bitrate == BAD_RATE_2MBPS)) {
			rate =
			    (struct ieee80211_rate *) piperp->rf->
			    getRate(AIROHA_55_MBPS_RATE_INDEX);
		}
	}

	return rate;
}

EXPORT_SYMBOL_GPL(piper_ps_check_rate);



/*
 * This routine restarts the transmitter after powering back
 * up, or failing to power down.
 *
 * 	1) Clear the power management bit so that frames will be
 *     sent indicating that we are poweredup.
 *  2) Set the flag to allow transmits again.
 *  3) If we stopped the mac80211 transmitter queue, then start
 *     it back up again.
 *  4) Notify the AP that we are awake by sending a null-data frame
 *     with the power management bit clear.
 */
static void ps_resume_transmits(struct piper_priv *piperp)
{
	piperp->ps.power_management = POWERED_UP;
 	piperp->ps.allowTransmits = true;
	piperp->ps.stopped_tx_queues = false;
 	piper_sendNullDataFrame(piperp, POWERED_UP);
	ieee80211_wake_queues(piperp->hw);
}






/*
 * This routine sets an event timer.  The ps_state_machine routine
 * will be executed when the event timer expires.
 */
static void ps_set_timer_event(struct piper_priv *piperp,
								enum piper_ps_event next_event,
								unsigned int timeout_ms)
{
	unsigned int delay_in_jiffies = MILLS_TO_JIFFIES(timeout_ms);

	if (delay_in_jiffies == 0)
		delay_in_jiffies++;

	del_timer_sync(&piperp->ps.timer);
	piperp->ps.this_event = next_event;
	piperp->ps.timer.expires = delay_in_jiffies + jiffies;
	add_timer(&piperp->ps.timer);
}


/*
 * This routine cancels an event timer set by ps_set_timer_event.
 */
static void ps_cancel_timer_event(struct piper_priv *piperp)
{
	del_timer_sync(&piperp->ps.timer);
}


#define WANT_STATE_MACHINE_DEBUG		(0)
#if WANT_STATE_MACHINE_DEBUG

struct event_record {
	enum piper_ps_event event;
	enum piper_ps_state state;
};

#define MAX_EVENTS_RECORDED		(15)
static unsigned int debug_events_index = 0;
static struct event_record debug_events[MAX_EVENTS_RECORDED];

void debug_track_event(enum piper_ps_event event, enum piper_ps_state state)
{
	debug_events[debug_events_index].event = event;
	debug_events[debug_events_index].state = state;
	if (debug_events_index == MAX_EVENTS_RECORDED) {
		unsigned int i;

		printk(KERN_ERR);
		for (i = 0; i < MAX_EVENTS_RECORDED; i++) {
			printk("(%d,%d)", debug_events[i].event,
					debug_events[i].state);
		}
		printk("\n");
		debug_events_index = 0;
	} else {
		debug_events_index++;
	}
}


#else
	#define debug_track_event(event, state)
#endif

/*
 * This is the entry point into the duty cycle state machine.  It may be called
 * by:
 *
 *		1) piper_ps_handle_beacon when a beacon frame is received.
 *		2) the receiver task when we receive the ACK for the last pending frame
 *		   when we are waiting to power down.
 *      3) by ps_timer for many timer events.
 */
static void ps_state_machine(struct piper_priv *piperp, enum piper_ps_event event)
{
	unsigned long flags;

	debug_track_event(event, piperp->ps.state);

	spin_lock_irqsave(&piperp->ps.lock, flags);


	switch (event) {
		case PS_EVENT_BEACON_RECEIVED:
			/*
			 * We just received a beacon.  This is really the driving event in this state
			 * machine.  Everything else is synchronized from it.
			 *
			 * We know we are powered up because we just received the beacon.  The normal
			 * control path is to set a timer which will expire when we need to start
			 * preparations for powering down again.
			 */
			ps_cancel_timer_event(piperp);				/* cancel missed beacon timer*/
			stats.receivedBeacons++;
			if (   (!piperp->areWeAssociated)
			    || (piperp->ps.beacon_int <  PS_MINIMUM_BEACON_INT)
			    || (piperp->ps.scan_timer != 0)) {
				/*
				 * Don't duty cyle while we are trying to associate.
				 */
				piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
				if (piperp->ps.scan_timer) {
					piperp->ps.scan_timer--;
				}
				break;
			}
			if (piperp->ps.state == PS_STATE_WAIT_FOR_BEACON) {
				int timeout;
				/*
				 * Calculate amount of time to sleep.
				 */
				piperp->ps.sleep_time = (piperp->ps.beacon_int * (100 - piperp->power_duty)) / 100;

				/*
				 * Now figure out how long we have before it's time to go to sleep.  We
				 * have to wake up at least PS_WAKE_BEFORE_BEACON_MS before we expect to
				 * receive the next beacon, and we need to start powering down at least
				 * PS_TRANSMITTER_SHUTDOWN_MS ahead of time.
				 */
				timeout = piperp->ps.beacon_int - (piperp->ps.sleep_time + PS_TRANSMITTER_SHUTDOWN_MS + PS_WAKE_BEFORE_BEACON_MS);
				if ((timeout > 0) && (piperp->ps.sleep_time > PS_MINIMUM_SLEEP_TIME)) {
					/*
					 * If there is enough time left that it makes sense to duty
					 * cycle, then program the timer and advance to the next state.
					 */
					piperp->ps.state = PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT;
					ps_set_timer_event(piperp, PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED, timeout);

					break;
				}
			} else {
				 if (   (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
				 	 || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
					ps_resume_transmits(piperp);
				}
				digi_dbg("*** Beacon event in state %d.\n", piperp->ps.state);
			}
			/*
			 * We will come here if we were in the wrong state when we received the
			 * beacon, or if the duty cycle is too short.
			 */
			piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
			ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
						  piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
			break;
		case PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED:
			/*
			 * This event is hit when it's time to start powering down.  Unfortunately, this is
			 * not a simple thing to do.  The first things to do are:
			 *
			 *  1. Set the power save on flag.  This will cause any transmit frame to be
			 * 	   sent with the power management bit set.
			 *  2. Stop the mac80211 layer from sending us anymore frames.
			 *  3. Signal the AP that we are powering down by sending a null-data frame with the
			 * 	   power management bit set.
			 */
			 if (piperp->ps.state == PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
			 	if (piperp->ps.scan_timer) {
			 		/*
			 		 * Don't shut down if we are scanning.
			 		 */
					piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
					break;
				}
			 	piperp->ps.power_management = POWERING_DOWN;
			 	piperp->ps.allowTransmits = false;
			 	ieee80211_stop_queues(piperp->hw);
			 	piperp->ps.stopped_tx_queues = true;
			 	piper_sendNullDataFrame(piperp, POWERING_DOWN);
			 	piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
				ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
							PS_TRANSMIT_TIMER_TICK_MS);
			} else {
				/*
				 * This should never happen (famous last words)
				 */
				digi_dbg("** stop tx event, state = %d.\n", piperp->ps.state);
				piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
				ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
							piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
			}
			break;
		case PS_EVENT_TRANSMITTER_DONE:
			/*
			 * This event is triggered when the receive task finishes processing the ACK
			 * from the null-data frame sent by the PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED event.
			 * We try to power down now.
			 */
			 if (piperp->ps.scan_timer == 0) {
				 if (   (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
				 	 || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
					ps_cancel_timer_event(piperp);				/* cancel transmitter done timeout timer*/
				 	if (piper_MacEnterSleepMode(piperp, PS_DONT_FORCE) == 0) {
				 		piperp->ps.state = PS_STATE_WAIT_FOR_WAKEUP_ALARM;
				 		/*
				 		 * Note that the value PS_EVENT_TRANSMITTER_DONE_TIMER_TICK is
				 		 * updated as necessary by the PS_EVENT_TRANSMITTER_DONE_TIMER_TICK
				 		 * event to take into account the amount of time it took for the
				 		 * transmitter to finish sending the last frame.
				 		 */
				 		ps_set_timer_event(piperp, PS_EVENT_WAKEUP, piperp->ps.sleep_time);
				 		break;
					}
				 } else {
#ifdef WANT_DEBUG
		printk(KERN_ERR "couldn't sleep, rxt=%d, AES busy = %d, txfifo=%d, txt=%d, rxfifo=%d\n",
				(piperp->ps.rxTaskletRunning),((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) != 0),
				(  (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0),
				(piperp->tx_tasklet_running),
    	     	(  (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
    			& BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0));
#endif
				 	digi_dbg("** PS_EVENT_TRANSMITTER_DONE event, but state == %d.\n", piperp->ps.state);
				}
			}
			 /*
			  * If we fall through to here, then either we are in the wrong state, or we were
			  * not able to power down the H/W.
			  */
			ps_resume_transmits(piperp);
			piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
			ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
						 piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
			break;
		case PS_EVENT_TRANSMITTER_DONE_TIMER_TICK:
			/*
			 * This event is triggered periodically while we are waiting for the
			 * transmitter to finish sending that last packet.  We decrement
			 * piperp->ps.sleep_time (which is used by the PS_EVENT_TRANSMITTER_DONE
			 * event).  If piperp->ps.sleep_time is still larger than our minimum
			 * required sleep time, then we just restart the timer.
			 */
			if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) {
				piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
				if (piperp->ps.sleep_time >= PS_MINIMUM_SLEEP_TIME) {
				 	piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
					ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
								PS_TRANSMIT_TIMER_TICK_MS);
				} else {
					/*
					 * Transmitter did not shut down in time.  Resume normal operations
					 * and stay awake until the next beacon.
					 */
					ps_resume_transmits(piperp);
				 	piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
					ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
								 piperp->ps.sleep_time + PS_WAKE_BEFORE_BEACON_MS
								 + PS_BEACON_TIMEOUT_MS);
				}
			} else if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT) {
				piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
				/*
				 * The piper_ps_rx_task_exiting routine sets this state just before it
				 * releases the lock on ps.state and calls this event.  If we ever
				 * come here, then the timer tick occurred just between the time
				 * piper_ps_rx_task_exiting released the lock and ps_state_machine
				 * reset it.  Since the tx done event is in progress, we should ignore
				 * this tick.
				 */
				break;
			} else {
				digi_dbg("** done tick in state %d.\n", piperp->ps.state);
			}
			break;
		case PS_EVENT_WAKEUP:
			/*
			 * This event is called when we have powered down and it is time
			 * to power back up again.
			 *
			 *	1) Power up the H/W.
			 *	2) Resume normal operations
			 *  3) Update our state.
			 *  4) Set a timeout for receiving the next beacom frame.
			 */
			if (piperp->ps.state == PS_STATE_WAIT_FOR_WAKEUP_ALARM) {
				piper_MacEnterActiveMode(piperp, PS_NO_SPIKE_SUPPRESSION);
				ps_resume_transmits(piperp);
			 	piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
				ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
							 PS_BEACON_TIMEOUT_MS + PS_WAKE_BEFORE_BEACON_MS);
			} else {
				digi_dbg("** wake event in state %d.\n", piperp->ps.state);
			}
			break;
		case PS_EVENT_MISSED_BEACON:
			/*
			 * This event is called when we miss a beacon.  For now just update
			 * our statistics.
			 */
		 	piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
		 	if ((piperp->areWeAssociated)  && (piperp->ps.scan_timer == 0)) {
				stats.missedBeacons++;
				ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, piperp->ps.beacon_int);
			}
			break;
		default:
			digi_dbg("**** ps_state_machine received unknown event %d.\n", event);
			break;
	}
	spin_unlock_irqrestore(&piperp->ps.lock, flags);

}

/*
 * This routine is called by the receiver task when it exits.  We use it to generate
 * the PS_EVENT_TRANSMITTER_DONE event.  The event signifies that the transmitter has
 * sent our null-data frame and is idle.  We determine this by checking frames_pending,
 * while will be nonzero if a null-data frame is waiting to be sent, and the machine's
 * state.
 */
void piper_ps_rx_task_exiting(struct piper_priv *piperp)
{
	unsigned long flags;

	spin_lock_irqsave(&piperp->ps.lock, flags);

	if (   (piperp->ps.frames_pending == 0)
		&& (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)) {
		/*
		 * We have a race condition between the transmitter done tick and
		 * this routine.  So this routine changes the state to
		 * PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT before it releases the
		 * lock so that we don't get confused.
		 */
		piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT;
		spin_unlock_irqrestore(&piperp->ps.lock, flags);
		ps_state_machine(piperp, PS_EVENT_TRANSMITTER_DONE);
	} else {
		spin_unlock_irqrestore(&piperp->ps.lock, flags);
	}
}

/*
 * Called when the event timer expires.  Call the state machine to handle
 * the event.
 */
static void ps_timer(unsigned long context)
{
	struct piper_priv *piperp = (struct piper_priv *) context;

	ps_state_machine(piperp, piperp->ps.this_event);
}



/*
 * This routine is called when we receive a beacon.  We extract the beacon interval
 * in case it has changed and then call the state machine.
 */
static void piper_ps_handle_beacon(struct piper_priv *piperp, struct sk_buff *skb)
{
#define BEACON_INT_LSB					(8)
#define BEACON_INT_MSB					(9)
	u32 beacon_int;
	_80211HeaderType *header = (_80211HeaderType *) skb->data;
	bool fromOurAp = piperp->areWeAssociated
					&& (memcmp(piperp->bssid, header->addr3, sizeof (header->addr3)) == 0);

	/*
	 * mac80211 does not inform us when the beacon interval changes, so we have
	 * to read this information from the beacon ourselves.
	 */

	if (fromOurAp) {
		beacon_int = skb->data[sizeof(_80211HeaderType) + BEACON_INT_LSB];
		beacon_int |= (skb->data[sizeof(_80211HeaderType) + BEACON_INT_MSB] << 8);
		piperp->ps.beacon_int = beacon_int;

		if (piperp->ps.mode == PS_MODE_LOW_POWER) {
			ps_state_machine(piperp, PS_EVENT_BEACON_RECEIVED);
		}
	}
}



/*
 * This routine is called when mac80211 starts doing things that might indicate it
 * is attempting to scan or reassociate.  Things like changing the channel or
 * disassociating.  When we receive an event like that, we stop duty cycling for
 * a while since it may interfere with attempts to reassociate with an access point.
 */
void piper_ps_scan_event(struct piper_priv *piperp)
{
	(void) piperp;
#if 0
	/*
	 * It appears that pausing duty cycling during association events may actually
	 * worsen performance I suspect that either the AP or mac80211 is measuring
	 * our throughput and adjusting the load accordingly, and that momentary changes
	 * in performance caused by pausing duty cyling interfere with this.
	 *
	 * TODO:  Consider removing this code.  I left it in for now in case we decide
	 *        to try it again, but if we're not going to use it, it just makes the
	 *        driver more confusing and should be removed.
	 */
	if (piperp->ps.beacon_int != 0) {
		piperp->ps.scan_timer = PS_SCAN_DELAY / piperp->ps.beacon_int;
	} else {
		piperp->ps.scan_timer = PS_SCAN_DELAY / 100;
	}
#endif
}



/*
 * This routine is called so we can process incoming frames.  We do the
 * handshaking to receive buffered frames in PS mode here.
 */
void piper_ps_process_receive_frame(struct piper_priv *piperp, struct sk_buff *skb)
{
	_80211HeaderType *header = (_80211HeaderType *) skb->data;

	if (header->fc.type == TYPE_BEACON) {
		piper_ps_handle_beacon(piperp, skb);
	} else if (   (header->fc.type == TYPE_ASSOC_RESP)
			   || (header->fc.type == TYPE_REASSOC_RESP)
			   || (header->fc.type == TYPE_PROBE_RESP)
			   || (header->fc.type == TYPE_DISASSOC)
			   || (header->fc.type == TYPE_DEAUTH)
			   || (header->fc.type == TYPE_ACTION)) {
		piper_ps_scan_event(piperp);
	}
}

EXPORT_SYMBOL_GPL(piper_ps_process_receive_frame);



/*
 * This function turns power save mode on or off.
 */
void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn)
{
#define MAX_SHUTDOWN_TIMEOUT		(100)
	unsigned long flags;

	spin_lock_irqsave(&piperp->ps.lock, flags);

	piper_ps_scan_event(piperp);
	if (powerSaveOn) {
		if (piperp->ps.beacon_int >= PS_MINIMUM_BEACON_INT) {
			if (piperp->ps.mode != PS_MODE_LOW_POWER) {
			    piperp->ps.aid = 0;
			    piperp->ps.mode = PS_MODE_LOW_POWER;
			    piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
			    piperp->ps.power_management = POWERED_UP;
			    piperp->ps.poweredDown = false;
			    piperp->ps.allowTransmits = true;
			    piperp->ps.stopped_tx_queues = false;
			    stats.receivedBeacons = 0;
			    stats.missedBeacons = 0;
				stats.modeStart = jiffies;
				stats.cycleStart = jiffies;
				stats.jiffiesOff = 0;
				stats.jiffiesOn = 0;
				piper_sendNullDataFrame(piperp, POWERED_UP);
				/*
				 * Will start it the next time we receive a beacon.
				 */
			}
		} else {
			printk(KERN_ERR
			       "\nUnable to set power save mode because the beacon \n"
			       "interval set on this access point less than 100ms.\n");
		}
	} else {
		ps_cancel_timer_event(piperp);
		if (piperp->ps.mode == PS_MODE_LOW_POWER) {
			piperp->ps.mode = PS_MODE_FULL_POWER;		/* stop duty cycle timer */
			if (piperp->ps.poweredDown) {
				/*
				 * If we were powered down, then power up and do the spike suppression.
				 */
				piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
			} else {
				unsigned int timeout = 50;
				int result;
				/*
				 * If we branch here, then we were already powered up.  You would think
				 * that we would be all set, but it's not that easy.  Piper has a bug in
				 * it where we have to run a special spike suppression routine when we
				 * power it up.  However, this routine takes an average of 30 ms to run,
				 * and I've see it take as long as 300 ms.  This is not acceptable when
				 * we are duty cycling every 100 ms.  To get around this, we do NOT do
				 * the spike suppression while duty cycling.  Instead, we simply avoid
				 * transmitting at those rates which would cause spikes.  Now, however,
				 * we are ending duty cycling and returning to normal operations so we
				 * have to do the spike suppression.  Since we are powered up, the first
				 * thing to do is to power down.
				 */
				if (piperp->ps.state != PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
					/*
					 * If we come here, then we did not happen to be trying to power down
					 * just as we got the command from mac80211, so we have to start
					 * the procedure.  This is the normal case.
					 *
					 * 1. Set the power save on flag.  This will cause frames to be
					 *    transmitted with the power management bit on.  The reason
					 *    for doing that is to tell the AP to stop sending us frames.
					 * 2. Stop the mac80211 layer from sending us more frames by stopping
					 *    the transmit queues.
					 * 3. Send a null-data frame to the AP with the power management bit
					 *    set.  This should cause it to stop sending us frames.
					 */
				 	piperp->ps.power_management = POWERING_DOWN;
				 	piperp->ps.allowTransmits = false;
				 	ieee80211_stop_queues(piperp->hw);
				 	piperp->ps.stopped_tx_queues = true;
				 	piper_sendNullDataFrame(piperp, POWERING_DOWN);
				}
				/*
				 * Now wait for that last frame to go and and then shut down.
				 */
				 result = -1;
				 for (timeout = 0; (timeout < MAX_SHUTDOWN_TIMEOUT) && (result != 0); timeout++) {
					spin_unlock_irqrestore(&piperp->ps.lock, flags);
					mdelay(10);
					spin_lock_irqsave(&piperp->ps.lock, flags);
					result = piper_MacEnterSleepMode(piperp, PS_DONT_FORCE);
				}
				if (result != 0) {
					/*
					 * This is bad.  For some reason we are not able to power down.  We
					 * will try to force it now, but this may end up putting the driver
					 * or H/W into a bad state.  However, we can't sit in the loop above
					 * forever either.
					 */
#ifdef WANT_DEBUG
					printk(KERN_ERR "Forcing Piper to power down\n");
					printk(KERN_ERR "BB_RSSI_EAS_BUSY = %d\n", piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY);
					printk(KERN_ERR "BB_GENERAL_CTL_TX_FIFO_EMPTY = %d\n",
					    piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_EMPTY);
					printk(KERN_ERR "BB_GENERAL_STAT_RX_FIFO_EMPTY = %d\n",
						piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) & BB_GENERAL_STAT_RX_FIFO_EMPTY);
					digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
#endif
					piper_MacEnterSleepMode(piperp, PS_FORCE_POWER_DOWN);
				}
				/*
				 * Wait a moment and then power the H/W back up and execute the spike suppression
				 * routine.
				 */
				spin_unlock_irqrestore(&piperp->ps.lock, flags);
				mdelay(30);
				spin_lock_irqsave(&piperp->ps.lock, flags);
				piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
				ps_resume_transmits(piperp);
			}
			stats.jiffiesOn += jiffies - stats.cycleStart;
#define WANT_STATS		(0)
#if WANT_STATS
			if ((piperp->ps.beacon_int != 0)
			    && ((jiffies - stats.modeStart) != 0)) {
				printk(KERN_ERR
				       "jiffiesOff = %u, jiffiesOn = %u, total time = %lu\n",
				       stats.jiffiesOff, stats.jiffiesOn,
				       (jiffies - stats.modeStart));
				printk(KERN_ERR
				       "Powered down %ld percent of the time.\n",
				       (stats.jiffiesOff * 100) / (jiffies - stats.modeStart));
				printk(KERN_ERR
				       "Received %u of %lu beacons while in powersave mode.\n",
				       stats.receivedBeacons,
				       (jiffies -
					stats.modeStart) /
				       MILLS_TO_JIFFIES(piperp->ps.beacon_int));
				printk(KERN_ERR "received %d beacons, missed %d\n",
						stats.receivedBeacons, stats.missedBeacons);
				printk(KERN_ERR "allowTransmits = %d, stopped_tx_queues = %d, q_count = %d\n",
 						 	piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues,
 						 	piperp->tx_queue_count);
				if ((stats.receivedBeacons + stats.missedBeacons) != 0)
					printk(KERN_ERR "%d%% beacons were missed\n",
						(100 * stats.missedBeacons) / (stats.receivedBeacons + stats.missedBeacons));
			}
#endif
		}
	    piperp->ps.aid = 0;
	    piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
	    piperp->ps.power_management = POWERED_UP;
	    piperp->ps.poweredDown = false;
	    piperp->ps.allowTransmits = true;
	    piperp->ps.stopped_tx_queues = false;
		ps_resume_transmits(piperp);
		piper_sendNullDataFrame(piperp, POWERED_UP);
	}

	spin_unlock_irqrestore(&piperp->ps.lock, flags);
}

EXPORT_SYMBOL_GPL(piper_ps_set);



/*
 * Called when driver is loaded.  Initialize our context.
 */
void piper_ps_init(struct piper_priv *piperp)
{
	memset(&piperp->ps, 0, sizeof(piperp->ps));
    piperp->ps.beacon_int = 100;
    piperp->ps.aid = 0;
	init_timer(&piperp->ps.timer);
	piperp->ps.timer.function = ps_timer;
	piperp->ps.timer.data = (unsigned long) piperp;
    piperp->ps.mode = PS_MODE_FULL_POWER;
    piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
	spin_lock_init(&piperp->ps.lock);
    piperp->ps.power_management = POWERED_UP;
    piperp->ps.poweredDown = false;
    piperp->ps.rxTaskletRunning;
    piperp->ps.allowTransmits = true;
    piperp->ps.stopped_tx_queues = false;
    piperp->ps.frames_pending = 0;
}

EXPORT_SYMBOL_GPL(piper_ps_init);


/*
 * Called when driver is unloaded.  Make sure the PS
 * timer is shut down.
 */
void piper_ps_deinit(struct piper_priv *piperp)
{
	piper_ps_set(piperp, true);
	piperp->ps.mode = PS_MODE_FULL_POWER;
	del_timer_sync(&piperp->ps.timer);
}

EXPORT_SYMBOL_GPL(piper_ps_deinit);