forked from analogdevicesinc/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
/
ipw2200.c
11909 lines (10099 loc) · 322 KB
/
ipw2200.c
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
/******************************************************************************
Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
802.11 status code portion of this file from ethereal-0.10.6:
Copyright 2000, Axis Communications AB
Ethereal - Network traffic analyzer
By Gerald Combs <[email protected]>
Copyright 1998 Gerald Combs
This program is free software; you can redistribute it and/or modify it
under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The full GNU General Public License is included in this distribution in the
file called LICENSE.
Contact Information:
James P. Ketrenos <[email protected]>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
******************************************************************************/
#include "ipw2200.h"
#include <linux/version.h>
#ifndef KBUILD_EXTMOD
#define VK "k"
#else
#define VK
#endif
#ifdef CONFIG_IPW2200_DEBUG
#define VD "d"
#else
#define VD
#endif
#ifdef CONFIG_IPW2200_MONITOR
#define VM "m"
#else
#define VM
#endif
#ifdef CONFIG_IPW2200_PROMISCUOUS
#define VP "p"
#else
#define VP
#endif
#ifdef CONFIG_IPW2200_RADIOTAP
#define VR "r"
#else
#define VR
#endif
#ifdef CONFIG_IPW2200_QOS
#define VQ "q"
#else
#define VQ
#endif
#define IPW2200_VERSION "1.2.0" VK VD VM VP VR VQ
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
#define DRV_VERSION IPW2200_VERSION
#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
static int cmdlog = 0;
static int debug = 0;
static int channel = 0;
static int mode = 0;
static u32 ipw_debug_level;
static int associate = 1;
static int auto_create = 1;
static int led = 0;
static int disable = 0;
static int bt_coexist = 0;
static int hwcrypto = 0;
static int roaming = 1;
static const char ipw_modes[] = {
'a', 'b', 'g', '?'
};
static int antenna = CFG_SYS_ANTENNA_BOTH;
#ifdef CONFIG_IPW2200_PROMISCUOUS
static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
#endif
#ifdef CONFIG_IPW2200_QOS
static int qos_enable = 0;
static int qos_burst_enable = 0;
static int qos_no_ack_mask = 0;
static int burst_duration_CCK = 0;
static int burst_duration_OFDM = 0;
static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
{QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
QOS_TX3_CW_MIN_OFDM},
{QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
QOS_TX3_CW_MAX_OFDM},
{QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
{QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
{QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
};
static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
{QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
QOS_TX3_CW_MIN_CCK},
{QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
QOS_TX3_CW_MAX_CCK},
{QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
{QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
{QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
QOS_TX3_TXOP_LIMIT_CCK}
};
static struct ieee80211_qos_parameters def_parameters_OFDM = {
{DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
DEF_TX3_CW_MIN_OFDM},
{DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
DEF_TX3_CW_MAX_OFDM},
{DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
{DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
{DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
};
static struct ieee80211_qos_parameters def_parameters_CCK = {
{DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
DEF_TX3_CW_MIN_CCK},
{DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
DEF_TX3_CW_MAX_CCK},
{DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
{DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
{DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
DEF_TX3_TXOP_LIMIT_CCK}
};
static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
static int from_priority_to_tx_queue[] = {
IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
};
static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
*qos_param);
static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
*qos_param);
#endif /* CONFIG_IPW2200_QOS */
static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
static void ipw_remove_current_network(struct ipw_priv *priv);
static void ipw_rx(struct ipw_priv *priv);
static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
struct clx2_tx_queue *txq, int qindex);
static int ipw_queue_reset(struct ipw_priv *priv);
static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
int len, int sync);
static void ipw_tx_queue_free(struct ipw_priv *);
static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
static void ipw_rx_queue_replenish(void *);
static int ipw_up(struct ipw_priv *);
static void ipw_bg_up(struct work_struct *work);
static void ipw_down(struct ipw_priv *);
static void ipw_bg_down(struct work_struct *work);
static int ipw_config(struct ipw_priv *);
static int init_supported_rates(struct ipw_priv *priv,
struct ipw_supported_rates *prates);
static void ipw_set_hwcrypto_keys(struct ipw_priv *);
static void ipw_send_wep_keys(struct ipw_priv *, int);
static int snprint_line(char *buf, size_t count,
const u8 * data, u32 len, u32 ofs)
{
int out, i, j, l;
char c;
out = snprintf(buf, count, "%08X", ofs);
for (l = 0, i = 0; i < 2; i++) {
out += snprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++)
out += snprintf(buf + out, count - out, "%02X ",
data[(i * 8 + j)]);
for (; j < 8; j++)
out += snprintf(buf + out, count - out, " ");
}
out += snprintf(buf + out, count - out, " ");
for (l = 0, i = 0; i < 2; i++) {
out += snprintf(buf + out, count - out, " ");
for (j = 0; j < 8 && l < len; j++, l++) {
c = data[(i * 8 + j)];
if (!isascii(c) || !isprint(c))
c = '.';
out += snprintf(buf + out, count - out, "%c", c);
}
for (; j < 8; j++)
out += snprintf(buf + out, count - out, " ");
}
return out;
}
static void printk_buf(int level, const u8 * data, u32 len)
{
char line[81];
u32 ofs = 0;
if (!(ipw_debug_level & level))
return;
while (len) {
snprint_line(line, sizeof(line), &data[ofs],
min(len, 16U), ofs);
printk(KERN_DEBUG "%s\n", line);
ofs += 16;
len -= min(len, 16U);
}
}
static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
{
size_t out = size;
u32 ofs = 0;
int total = 0;
while (size && len) {
out = snprint_line(output, size, &data[ofs],
min_t(size_t, len, 16U), ofs);
ofs += 16;
output += out;
size -= out;
len -= min_t(size_t, len, 16U);
total += out;
}
return total;
}
/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
{
IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg8(a, b, c);
}
/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
{
IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg16(a, b, c);
}
/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
{
IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
__LINE__, (u32) (b), (u32) (c));
_ipw_write_reg32(a, b, c);
}
/* 8-bit direct write (low 4K) */
#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write8(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write8(ipw, ofs, val)
/* 16-bit direct write (low 4K) */
#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write16(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write16(ipw, ofs, val)
/* 32-bit direct write (low 4K) */
#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
#define ipw_write32(ipw, ofs, val) \
IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
_ipw_write32(ipw, ofs, val)
/* 8-bit direct read (low 4K) */
#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
/* 8-bit direct read (low 4K), with debug wrapper */
static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
return _ipw_read8(ipw, ofs);
}
/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
/* 16-bit direct read (low 4K) */
#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
/* 16-bit direct read (low 4K), with debug wrapper */
static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
return _ipw_read16(ipw, ofs);
}
/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
/* 32-bit direct read (low 4K) */
#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
/* 32-bit direct read (low 4K), with debug wrapper */
static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
{
IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
return _ipw_read32(ipw, ofs);
}
/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
/* multi-byte read (above 4K), with debug wrapper */
static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
static inline void __ipw_read_indirect(const char *f, int l,
struct ipw_priv *a, u32 b, u8 * c, int d)
{
IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
d);
_ipw_read_indirect(a, b, c, d);
}
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
int num);
#define ipw_write_indirect(a, b, c, d) \
IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
_ipw_write_indirect(a, b, c, d)
/* 32-bit indirect write (above 4K) */
static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
{
IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
_ipw_write32(priv, IPW_INDIRECT_DATA, value);
}
/* 8-bit indirect write (above 4K) */
static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
{
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
u32 dif_len = reg - aligned_addr;
IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
_ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
}
/* 16-bit indirect write (above 4K) */
static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
{
u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
u32 dif_len = (reg - aligned_addr) & (~0x1ul);
IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
_ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
}
/* 8-bit indirect read (above 4K) */
static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
{
u32 word;
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
word = _ipw_read32(priv, IPW_INDIRECT_DATA);
return (word >> ((reg & 0x3) * 8)) & 0xff;
}
/* 32-bit indirect read (above 4K) */
static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
{
u32 value;
IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
_ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
value = _ipw_read32(priv, IPW_INDIRECT_DATA);
IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
return value;
}
/* General purpose, no alignment requirement, iterative (multi-byte) read, */
/* for area above 1st 4K of SRAM/reg space */
static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
u32 dif_len = addr - aligned_addr;
u32 i;
IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
if (num <= 0) {
return;
}
/* Read the first dword (or portion) byte by byte */
if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
/* Start reading at aligned_addr + dif_len */
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
*buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
aligned_addr += 4;
}
/* Read all of the middle dwords as dwords, with auto-increment */
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
*(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
/* Read the last dword (or portion) byte by byte */
if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--)
*buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
}
}
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/* for area above 1st 4K of SRAM/reg space */
static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
int num)
{
u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
u32 dif_len = addr - aligned_addr;
u32 i;
IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
if (num <= 0) {
return;
}
/* Write the first dword (or portion) byte by byte */
if (unlikely(dif_len)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
/* Start writing at aligned_addr + dif_len */
for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
aligned_addr += 4;
}
/* Write all of the middle dwords as dwords, with auto-increment */
_ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
_ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
/* Write the last dword (or portion) byte by byte */
if (unlikely(num)) {
_ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
for (i = 0; num > 0; i++, num--, buf++)
_ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
}
}
/* General purpose, no alignment requirement, iterative (multi-byte) write, */
/* for 1st 4K of SRAM/regs space */
static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
int num)
{
memcpy_toio((priv->hw_base + addr), buf, num);
}
/* Set bit(s) in low 4K of SRAM/regs */
static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
}
/* Clear bit(s) in low 4K of SRAM/regs */
static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
{
ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
}
static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
{
if (priv->status & STATUS_INT_ENABLED)
return;
priv->status |= STATUS_INT_ENABLED;
ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
}
static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
{
if (!(priv->status & STATUS_INT_ENABLED))
return;
priv->status &= ~STATUS_INT_ENABLED;
ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
}
static inline void ipw_enable_interrupts(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->irq_lock, flags);
__ipw_enable_interrupts(priv);
spin_unlock_irqrestore(&priv->irq_lock, flags);
}
static inline void ipw_disable_interrupts(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->irq_lock, flags);
__ipw_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->irq_lock, flags);
}
static char *ipw_error_desc(u32 val)
{
switch (val) {
case IPW_FW_ERROR_OK:
return "ERROR_OK";
case IPW_FW_ERROR_FAIL:
return "ERROR_FAIL";
case IPW_FW_ERROR_MEMORY_UNDERFLOW:
return "MEMORY_UNDERFLOW";
case IPW_FW_ERROR_MEMORY_OVERFLOW:
return "MEMORY_OVERFLOW";
case IPW_FW_ERROR_BAD_PARAM:
return "BAD_PARAM";
case IPW_FW_ERROR_BAD_CHECKSUM:
return "BAD_CHECKSUM";
case IPW_FW_ERROR_NMI_INTERRUPT:
return "NMI_INTERRUPT";
case IPW_FW_ERROR_BAD_DATABASE:
return "BAD_DATABASE";
case IPW_FW_ERROR_ALLOC_FAIL:
return "ALLOC_FAIL";
case IPW_FW_ERROR_DMA_UNDERRUN:
return "DMA_UNDERRUN";
case IPW_FW_ERROR_DMA_STATUS:
return "DMA_STATUS";
case IPW_FW_ERROR_DINO_ERROR:
return "DINO_ERROR";
case IPW_FW_ERROR_EEPROM_ERROR:
return "EEPROM_ERROR";
case IPW_FW_ERROR_SYSASSERT:
return "SYSASSERT";
case IPW_FW_ERROR_FATAL_ERROR:
return "FATAL_ERROR";
default:
return "UNKNOWN_ERROR";
}
}
static void ipw_dump_error_log(struct ipw_priv *priv,
struct ipw_fw_error *error)
{
u32 i;
if (!error) {
IPW_ERROR("Error allocating and capturing error log. "
"Nothing to dump.\n");
return;
}
IPW_ERROR("Start IPW Error Log Dump:\n");
IPW_ERROR("Status: 0x%08X, Config: %08X\n",
error->status, error->config);
for (i = 0; i < error->elem_len; i++)
IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
ipw_error_desc(error->elem[i].desc),
error->elem[i].time,
error->elem[i].blink1,
error->elem[i].blink2,
error->elem[i].link1,
error->elem[i].link2, error->elem[i].data);
for (i = 0; i < error->log_len; i++)
IPW_ERROR("%i\t0x%08x\t%i\n",
error->log[i].time,
error->log[i].data, error->log[i].event);
}
static inline int ipw_is_init(struct ipw_priv *priv)
{
return (priv->status & STATUS_INIT) ? 1 : 0;
}
static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
{
u32 addr, field_info, field_len, field_count, total_len;
IPW_DEBUG_ORD("ordinal = %i\n", ord);
if (!priv || !val || !len) {
IPW_DEBUG_ORD("Invalid argument\n");
return -EINVAL;
}
/* verify device ordinal tables have been initialized */
if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
IPW_DEBUG_ORD("Access ordinals before initialization\n");
return -EINVAL;
}
switch (IPW_ORD_TABLE_ID_MASK & ord) {
case IPW_ORD_TABLE_0_MASK:
/*
* TABLE 0: Direct access to a table of 32 bit values
*
* This is a very simple table with the data directly
* read from the table
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table0_len) {
IPW_DEBUG_ORD("ordinal value (%i) longer then "
"max (%i)\n", ord, priv->table0_len);
return -EINVAL;
}
/* verify we have enough room to store the value */
if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, "
"need %zd\n", sizeof(u32));
return -EINVAL;
}
IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
ord, priv->table0_addr + (ord << 2));
*len = sizeof(u32);
ord <<= 2;
*((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
break;
case IPW_ORD_TABLE_1_MASK:
/*
* TABLE 1: Indirect access to a table of 32 bit values
*
* This is a fairly large table of u32 values each
* representing starting addr for the data (which is
* also a u32)
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table1_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
/* verify we have enough room to store the value */
if (*len < sizeof(u32)) {
IPW_DEBUG_ORD("ordinal buffer length too small, "
"need %zd\n", sizeof(u32));
return -EINVAL;
}
*((u32 *) val) =
ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
*len = sizeof(u32);
break;
case IPW_ORD_TABLE_2_MASK:
/*
* TABLE 2: Indirect access to a table of variable sized values
*
* This table consist of six values, each containing
* - dword containing the starting offset of the data
* - dword containing the lengh in the first 16bits
* and the count in the second 16bits
*/
/* remove the table id from the ordinal */
ord &= IPW_ORD_TABLE_VALUE_MASK;
/* boundary check */
if (ord > priv->table2_len) {
IPW_DEBUG_ORD("ordinal value too long\n");
return -EINVAL;
}
/* get the address of statistic */
addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
/* get the second DW of statistics ;
* two 16-bit words - first is length, second is count */
field_info =
ipw_read_reg32(priv,
priv->table2_addr + (ord << 3) +
sizeof(u32));
/* get each entry length */
field_len = *((u16 *) & field_info);
/* get number of entries */
field_count = *(((u16 *) & field_info) + 1);
/* abort if not enought memory */
total_len = field_len * field_count;
if (total_len > *len) {
*len = total_len;
return -EINVAL;
}
*len = total_len;
if (!total_len)
return 0;
IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
"field_info = 0x%08x\n",
addr, total_len, field_info);
ipw_read_indirect(priv, addr, val, total_len);
break;
default:
IPW_DEBUG_ORD("Invalid ordinal!\n");
return -EINVAL;
}
return 0;
}
static void ipw_init_ordinals(struct ipw_priv *priv)
{
priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
priv->table0_len = ipw_read32(priv, priv->table0_addr);
IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
priv->table0_addr, priv->table0_len);
priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
priv->table1_addr, priv->table1_len);
priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
priv->table2_len &= 0x0000ffff; /* use first two bytes */
IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
priv->table2_addr, priv->table2_len);
}
static u32 ipw_register_toggle(u32 reg)
{
reg &= ~IPW_START_STANDBY;
if (reg & IPW_GATE_ODMA)
reg &= ~IPW_GATE_ODMA;
if (reg & IPW_GATE_IDMA)
reg &= ~IPW_GATE_IDMA;
if (reg & IPW_GATE_ADMA)
reg &= ~IPW_GATE_ADMA;
return reg;
}
/*
* LED behavior:
* - On radio ON, turn on any LEDs that require to be on during start
* - On initialization, start unassociated blink
* - On association, disable unassociated blink
* - On disassociation, start unassociated blink
* - On radio OFF, turn off any LEDs started during radio on
*
*/
#define LD_TIME_LINK_ON msecs_to_jiffies(300)
#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
#define LD_TIME_ACT_ON msecs_to_jiffies(250)
static void ipw_led_link_on(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
/* If configured to not use LEDs, or nic_type is 1,
* then we don't toggle a LINK led */
if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
return;
spin_lock_irqsave(&priv->lock, flags);
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_LED_LINK_ON)) {
IPW_DEBUG_LED("Link LED On\n");
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led |= priv->led_association_on;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
priv->status |= STATUS_LED_LINK_ON;
/* If we aren't associated, schedule turning the LED off */
if (!(priv->status & STATUS_ASSOCIATED))
queue_delayed_work(priv->workqueue,
&priv->led_link_off,
LD_TIME_LINK_ON);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_link_on(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_link_on.work);
mutex_lock(&priv->mutex);
ipw_led_link_on(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_led_link_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
/* If configured not to use LEDs, or nic type is 1,
* then we don't goggle the LINK led. */
if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
return;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_LED_LINK_ON) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_association_off;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Link LED Off\n");
priv->status &= ~STATUS_LED_LINK_ON;
/* If we aren't associated and the radio is on, schedule
* turning the LED on (blink while unassociated) */
if (!(priv->status & STATUS_RF_KILL_MASK) &&
!(priv->status & STATUS_ASSOCIATED))
queue_delayed_work(priv->workqueue, &priv->led_link_on,
LD_TIME_LINK_OFF);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_link_off(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_link_off.work);
mutex_lock(&priv->mutex);
ipw_led_link_off(priv);
mutex_unlock(&priv->mutex);
}
static void __ipw_led_activity_on(struct ipw_priv *priv)
{
u32 led;
if (priv->config & CFG_NO_LED)
return;
if (priv->status & STATUS_RF_KILL_MASK)
return;
if (!(priv->status & STATUS_LED_ACT_ON)) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led |= priv->led_activity_on;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Activity LED On\n");
priv->status |= STATUS_LED_ACT_ON;
cancel_delayed_work(&priv->led_act_off);
queue_delayed_work(priv->workqueue, &priv->led_act_off,
LD_TIME_ACT_ON);
} else {
/* Reschedule LED off for full time period */
cancel_delayed_work(&priv->led_act_off);
queue_delayed_work(priv->workqueue, &priv->led_act_off,
LD_TIME_ACT_ON);
}
}
#if 0
void ipw_led_activity_on(struct ipw_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
__ipw_led_activity_on(priv);
spin_unlock_irqrestore(&priv->lock, flags);
}
#endif /* 0 */
static void ipw_led_activity_off(struct ipw_priv *priv)
{
unsigned long flags;
u32 led;
if (priv->config & CFG_NO_LED)
return;
spin_lock_irqsave(&priv->lock, flags);
if (priv->status & STATUS_LED_ACT_ON) {
led = ipw_read_reg32(priv, IPW_EVENT_REG);
led &= priv->led_activity_off;
led = ipw_register_toggle(led);
IPW_DEBUG_LED("Reg: 0x%08X\n", led);
ipw_write_reg32(priv, IPW_EVENT_REG, led);
IPW_DEBUG_LED("Activity LED Off\n");
priv->status &= ~STATUS_LED_ACT_ON;
}
spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipw_bg_led_activity_off(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, led_act_off.work);
mutex_lock(&priv->mutex);
ipw_led_activity_off(priv);
mutex_unlock(&priv->mutex);
}
static void ipw_led_band_on(struct ipw_priv *priv)
{