Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
42774986
Commit
42774986
authored
Feb 23, 2011
by
John W. Linville
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-linville' of
git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
parents
9077f218
b622d992
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
478 additions
and
77 deletions
+478
-77
drivers/net/wireless/wl12xx/acx.c
drivers/net/wireless/wl12xx/acx.c
+26
-2
drivers/net/wireless/wl12xx/acx.h
drivers/net/wireless/wl12xx/acx.h
+9
-0
drivers/net/wireless/wl12xx/init.c
drivers/net/wireless/wl12xx/init.c
+1
-1
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/main.c
+118
-29
drivers/net/wireless/wl12xx/ps.c
drivers/net/wireless/wl12xx/ps.c
+78
-0
drivers/net/wireless/wl12xx/ps.h
drivers/net/wireless/wl12xx/ps.h
+2
-0
drivers/net/wireless/wl12xx/rx.c
drivers/net/wireless/wl12xx/rx.c
+3
-3
drivers/net/wireless/wl12xx/rx.h
drivers/net/wireless/wl12xx/rx.h
+0
-4
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/tx.c
+201
-36
drivers/net/wireless/wl12xx/tx.h
drivers/net/wireless/wl12xx/tx.h
+3
-2
drivers/net/wireless/wl12xx/wl12xx.h
drivers/net/wireless/wl12xx/wl12xx.h
+37
-0
No files found.
drivers/net/wireless/wl12xx/acx.c
View file @
42774986
...
...
@@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
/* get data from A-MPDU parameters field */
acx
->
ampdu_max_length
=
ht_cap
->
ampdu_factor
;
acx
->
ampdu_min_spacing
=
ht_cap
->
ampdu_density
;
memcpy
(
acx
->
mac_address
,
mac_address
,
ETH_ALEN
);
}
memcpy
(
acx
->
mac_address
,
mac_address
,
ETH_ALEN
);
acx
->
ht_capabilites
=
cpu_to_le32
(
ht_capabilites
);
ret
=
wl1271_cmd_configure
(
wl
,
ACX_PEER_HT_CAP
,
acx
,
sizeof
(
*
acx
));
...
...
@@ -1542,3 +1541,28 @@ int wl1271_acx_config_ps(struct wl1271 *wl)
kfree
(
config_ps
);
return
ret
;
}
int
wl1271_acx_set_inconnection_sta
(
struct
wl1271
*
wl
,
u8
*
addr
)
{
struct
wl1271_acx_inconnection_sta
*
acx
=
NULL
;
int
ret
;
wl1271_debug
(
DEBUG_ACX
,
"acx set inconnaction sta %pM"
,
addr
);
acx
=
kzalloc
(
sizeof
(
*
acx
),
GFP_KERNEL
);
if
(
!
acx
)
return
-
ENOMEM
;
memcpy
(
acx
->
addr
,
addr
,
ETH_ALEN
);
ret
=
wl1271_cmd_configure
(
wl
,
ACX_UPDATE_INCONNECTION_STA_LIST
,
acx
,
sizeof
(
*
acx
));
if
(
ret
<
0
)
{
wl1271_warning
(
"acx set inconnaction sta failed: %d"
,
ret
);
goto
out
;
}
out:
kfree
(
acx
);
return
ret
;
}
drivers/net/wireless/wl12xx/acx.h
View file @
42774986
...
...
@@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps {
__le32
null_data_rate
;
}
__packed
;
struct
wl1271_acx_inconnection_sta
{
struct
acx_header
header
;
u8
addr
[
ETH_ALEN
];
u8
padding1
[
2
];
}
__packed
;
enum
{
ACX_WAKE_UP_CONDITIONS
=
0x0002
,
ACX_MEM_CFG
=
0x0003
,
...
...
@@ -1215,6 +1222,7 @@ enum {
ACX_GEN_FW_CMD
=
0x0070
,
ACX_HOST_IF_CFG_BITMAP
=
0x0071
,
ACX_MAX_TX_FAILURE
=
0x0072
,
ACX_UPDATE_INCONNECTION_STA_LIST
=
0x0073
,
DOT11_RX_MSDU_LIFE_TIME
=
0x1004
,
DOT11_CUR_TX_PWR
=
0x100D
,
DOT11_RX_DOT11_MODE
=
0x1012
,
...
...
@@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn,
int
wl1271_acx_tsf_info
(
struct
wl1271
*
wl
,
u64
*
mactime
);
int
wl1271_acx_max_tx_retry
(
struct
wl1271
*
wl
);
int
wl1271_acx_config_ps
(
struct
wl1271
*
wl
);
int
wl1271_acx_set_inconnection_sta
(
struct
wl1271
*
wl
,
u8
*
addr
);
#endif
/* __WL1271_ACX_H__ */
drivers/net/wireless/wl12xx/init.c
View file @
42774986
...
...
@@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl)
static
int
wl1271_set_ba_policies
(
struct
wl1271
*
wl
)
{
u8
tid_index
;
u8
ret
=
0
;
int
ret
=
0
;
/* Reset the BA RX indicators */
wl
->
ba_rx_bitmap
=
0
;
...
...
drivers/net/wireless/wl12xx/main.c
View file @
42774986
...
...
@@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl)
if
(
ret
<
0
)
goto
out_free_memmap
;
ret
=
wl1271_acx_sta_mem_cfg
(
wl
);
if
(
ret
<
0
)
goto
out_free_memmap
;
/* Default fragmentation threshold */
ret
=
wl1271_acx_frag_threshold
(
wl
,
wl
->
conf
.
tx
.
frag_threshold
);
if
(
ret
<
0
)
...
...
@@ -533,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl)
return
ret
;
}
static
void
wl1271_irq_ps_regulate_link
(
struct
wl1271
*
wl
,
u8
hlid
,
u8
tx_blks
)
{
bool
fw_ps
;
/* only regulate station links */
if
(
hlid
<
WL1271_AP_STA_HLID_START
)
return
;
fw_ps
=
test_bit
(
hlid
,
(
unsigned
long
*
)
&
wl
->
ap_fw_ps_map
);
/*
* Wake up from high level PS if the STA is asleep with too little
* blocks in FW or if the STA is awake.
*/
if
(
!
fw_ps
||
tx_blks
<
WL1271_PS_STA_MAX_BLOCKS
)
wl1271_ps_link_end
(
wl
,
hlid
);
/* Start high-level PS if the STA is asleep with enough blocks in FW */
else
if
(
fw_ps
&&
tx_blks
>=
WL1271_PS_STA_MAX_BLOCKS
)
wl1271_ps_link_start
(
wl
,
hlid
,
true
);
}
static
void
wl1271_irq_update_links_status
(
struct
wl1271
*
wl
,
struct
wl1271_fw_ap_status
*
status
)
{
u32
cur_fw_ps_map
;
u8
hlid
;
cur_fw_ps_map
=
le32_to_cpu
(
status
->
link_ps_bitmap
);
if
(
wl
->
ap_fw_ps_map
!=
cur_fw_ps_map
)
{
wl1271_debug
(
DEBUG_PSM
,
"link ps prev 0x%x cur 0x%x changed 0x%x"
,
wl
->
ap_fw_ps_map
,
cur_fw_ps_map
,
wl
->
ap_fw_ps_map
^
cur_fw_ps_map
);
wl
->
ap_fw_ps_map
=
cur_fw_ps_map
;
}
for
(
hlid
=
WL1271_AP_STA_HLID_START
;
hlid
<
AP_MAX_LINKS
;
hlid
++
)
{
u8
cnt
=
status
->
tx_lnk_free_blks
[
hlid
]
-
wl
->
links
[
hlid
].
prev_freed_blks
;
wl
->
links
[
hlid
].
prev_freed_blks
=
status
->
tx_lnk_free_blks
[
hlid
];
wl
->
links
[
hlid
].
allocated_blks
-=
cnt
;
wl1271_irq_ps_regulate_link
(
wl
,
hlid
,
wl
->
links
[
hlid
].
allocated_blks
);
}
}
static
void
wl1271_fw_status
(
struct
wl1271
*
wl
,
struct
wl1271_fw_full_status
*
full_status
)
{
...
...
@@ -570,6 +625,10 @@ static void wl1271_fw_status(struct wl1271 *wl,
if
(
total
)
clear_bit
(
WL1271_FLAG_FW_TX_BUSY
,
&
wl
->
flags
);
/* for AP update num of allocated TX blocks per link and ps status */
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
wl1271_irq_update_links_status
(
wl
,
&
full_status
->
ap
);
/* update the host-chipset time offset */
getnstimeofday
(
&
ts
);
wl
->
time_offset
=
(
timespec_to_ns
(
&
ts
)
>>
10
)
-
...
...
@@ -980,14 +1039,32 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
struct
wl1271
*
wl
=
hw
->
priv
;
unsigned
long
flags
;
int
q
;
u8
hlid
=
0
;
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
wl
->
tx_queue_count
++
;
/*
* The workqueue is slow to process the tx_queue and we need stop
* the queue here, otherwise the queue will get too long.
*/
if
(
wl
->
tx_queue_count
>=
WL1271_TX_QUEUE_HIGH_WATERMARK
)
{
wl1271_debug
(
DEBUG_TX
,
"op_tx: stopping queues"
);
ieee80211_stop_queues
(
wl
->
hw
);
set_bit
(
WL1271_FLAG_TX_QUEUE_STOPPED
,
&
wl
->
flags
);
}
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
/* queue the packet */
q
=
wl1271_tx_get_queue
(
skb_get_queue_mapping
(
skb
));
skb_queue_tail
(
&
wl
->
tx_queue
[
q
],
skb
);
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
{
hlid
=
wl1271_tx_get_hlid
(
skb
);
wl1271_debug
(
DEBUG_TX
,
"queue skb hlid %d q %d"
,
hlid
,
q
);
skb_queue_tail
(
&
wl
->
links
[
hlid
].
tx_queue
[
q
],
skb
);
}
else
{
skb_queue_tail
(
&
wl
->
tx_queue
[
q
],
skb
);
}
/*
* The chip specific setup must run before the first TX packet -
...
...
@@ -997,19 +1074,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if
(
!
test_bit
(
WL1271_FLAG_FW_TX_BUSY
,
&
wl
->
flags
))
ieee80211_queue_work
(
wl
->
hw
,
&
wl
->
tx_work
);
/*
* The workqueue is slow to process the tx_queue and we need stop
* the queue here, otherwise the queue will get too long.
*/
if
(
wl
->
tx_queue_count
>=
WL1271_TX_QUEUE_HIGH_WATERMARK
)
{
wl1271_debug
(
DEBUG_TX
,
"op_tx: stopping queues"
);
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
ieee80211_stop_queues
(
wl
->
hw
);
set_bit
(
WL1271_FLAG_TX_QUEUE_STOPPED
,
&
wl
->
flags
);
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
}
return
NETDEV_TX_OK
;
}
...
...
@@ -1221,6 +1285,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl
->
filters
=
0
;
wl1271_free_ap_keys
(
wl
);
memset
(
wl
->
ap_hlid_map
,
0
,
sizeof
(
wl
->
ap_hlid_map
));
wl
->
ap_fw_ps_map
=
0
;
wl
->
ap_ps_map
=
0
;
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
wl
->
tx_blocks_freed
[
i
]
=
0
;
...
...
@@ -2218,6 +2284,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
u32
sta_rate_set
=
0
;
int
ret
;
struct
ieee80211_sta
*
sta
;
bool
sta_exists
=
false
;
struct
ieee80211_sta_ht_cap
sta_ht_cap
;
if
(
is_ibss
)
{
ret
=
wl1271_bss_beacon_info_changed
(
wl
,
vif
,
bss_conf
,
...
...
@@ -2289,16 +2357,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
if
(
sta
->
ht_cap
.
ht_supported
)
sta_rate_set
|=
(
sta
->
ht_cap
.
mcs
.
rx_mask
[
0
]
<<
HW_HT_RATES_OFFSET
);
sta_ht_cap
=
sta
->
ht_cap
;
sta_exists
=
true
;
}
rcu_read_unlock
();
if
(
sta_exists
)
{
/* handle new association with HT and HT information change */
if
((
changed
&
BSS_CHANGED_HT
)
&&
(
bss_conf
->
channel_type
!=
NL80211_CHAN_NO_HT
))
{
ret
=
wl1271_acx_set_ht_capabilities
(
wl
,
&
sta
->
ht_cap
,
ret
=
wl1271_acx_set_ht_capabilities
(
wl
,
&
sta
_
ht_cap
,
true
);
if
(
ret
<
0
)
{
wl1271_warning
(
"Set ht cap true failed %d"
,
ret
);
rcu_read_unlock
();
goto
out
;
}
ret
=
wl1271_acx_set_ht_information
(
wl
,
...
...
@@ -2306,23 +2378,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
if
(
ret
<
0
)
{
wl1271_warning
(
"Set ht information failed %d"
,
ret
);
rcu_read_unlock
();
goto
out
;
}
}
/* handle new association without HT and disassociation */
else
if
(
changed
&
BSS_CHANGED_ASSOC
)
{
ret
=
wl1271_acx_set_ht_capabilities
(
wl
,
&
sta
->
ht_cap
,
ret
=
wl1271_acx_set_ht_capabilities
(
wl
,
&
sta
_
ht_cap
,
false
);
if
(
ret
<
0
)
{
wl1271_warning
(
"Set ht cap false failed %d"
,
ret
);
rcu_read_unlock
();
goto
out
;
}
}
}
rcu_read_unlock
();
if
((
changed
&
BSS_CHANGED_ASSOC
))
{
if
(
bss_conf
->
assoc
)
{
...
...
@@ -2612,7 +2681,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
return
0
;
}
static
int
wl1271_allocate_
hlid
(
struct
wl1271
*
wl
,
static
int
wl1271_allocate_
sta
(
struct
wl1271
*
wl
,
struct
ieee80211_sta
*
sta
,
u8
*
hlid
)
{
...
...
@@ -2626,18 +2695,25 @@ static int wl1271_allocate_hlid(struct wl1271 *wl,
}
wl_sta
=
(
struct
wl1271_station
*
)
sta
->
drv_priv
;
__set_bit
(
id
,
wl
->
ap_hlid_map
);
wl_sta
->
hlid
=
WL1271_AP_STA_HLID_START
+
id
;
*
hlid
=
wl_sta
->
hlid
;
memcpy
(
wl
->
links
[
wl_sta
->
hlid
].
addr
,
sta
->
addr
,
ETH_ALEN
);
return
0
;
}
static
void
wl1271_free_
hlid
(
struct
wl1271
*
wl
,
u8
hlid
)
static
void
wl1271_free_
sta
(
struct
wl1271
*
wl
,
u8
hlid
)
{
int
id
=
hlid
-
WL1271_AP_STA_HLID_START
;
if
(
WARN_ON
(
!
test_bit
(
id
,
wl
->
ap_hlid_map
)))
return
;
__clear_bit
(
id
,
wl
->
ap_hlid_map
);
memset
(
wl
->
links
[
hlid
].
addr
,
0
,
ETH_ALEN
);
wl1271_tx_reset_link_queues
(
wl
,
hlid
);
__clear_bit
(
hlid
,
&
wl
->
ap_ps_map
);
__clear_bit
(
hlid
,
(
unsigned
long
*
)
&
wl
->
ap_fw_ps_map
);
}
static
int
wl1271_op_sta_add
(
struct
ieee80211_hw
*
hw
,
...
...
@@ -2658,13 +2734,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
wl1271_debug
(
DEBUG_MAC80211
,
"mac80211 add sta %d"
,
(
int
)
sta
->
aid
);
ret
=
wl1271_allocate_
hlid
(
wl
,
sta
,
&
hlid
);
ret
=
wl1271_allocate_
sta
(
wl
,
sta
,
&
hlid
);
if
(
ret
<
0
)
goto
out
;
ret
=
wl1271_ps_elp_wakeup
(
wl
,
false
);
if
(
ret
<
0
)
goto
out
;
goto
out
_free_sta
;
ret
=
wl1271_cmd_add_sta
(
wl
,
sta
,
hlid
);
if
(
ret
<
0
)
...
...
@@ -2673,6 +2749,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
out_sleep:
wl1271_ps_elp_sleep
(
wl
);
out_free_sta:
if
(
ret
<
0
)
wl1271_free_sta
(
wl
,
hlid
);
out:
mutex_unlock
(
&
wl
->
mutex
);
return
ret
;
...
...
@@ -2709,7 +2789,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
if
(
ret
<
0
)
goto
out_sleep
;
wl1271_free_
hlid
(
wl
,
wl_sta
->
hlid
);
wl1271_free_
sta
(
wl
,
wl_sta
->
hlid
);
out_sleep:
wl1271_ps_elp_sleep
(
wl
);
...
...
@@ -3212,7 +3292,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
IEEE80211_HW_SUPPORTS_UAPSD
|
IEEE80211_HW_HAS_RATE_CONTROL
|
IEEE80211_HW_CONNECTION_MONITOR
|
IEEE80211_HW_SUPPORTS_CQM_RSSI
;
IEEE80211_HW_SUPPORTS_CQM_RSSI
|
IEEE80211_HW_REPORTS_TX_ACK_STATUS
|
IEEE80211_HW_AP_LINK_PS
;
wl
->
hw
->
wiphy
->
cipher_suites
=
cipher_suites
;
wl
->
hw
->
wiphy
->
n_cipher_suites
=
ARRAY_SIZE
(
cipher_suites
);
...
...
@@ -3264,7 +3346,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
struct
ieee80211_hw
*
hw
;
struct
platform_device
*
plat_dev
=
NULL
;
struct
wl1271
*
wl
;
int
i
,
ret
;
int
i
,
j
,
ret
;
unsigned
int
order
;
hw
=
ieee80211_alloc_hw
(
sizeof
(
*
wl
),
&
wl1271_ops
);
...
...
@@ -3292,6 +3374,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
skb_queue_head_init
(
&
wl
->
tx_queue
[
i
]);
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
for
(
j
=
0
;
j
<
AP_MAX_LINKS
;
j
++
)
skb_queue_head_init
(
&
wl
->
links
[
j
].
tx_queue
[
i
]);
INIT_DELAYED_WORK
(
&
wl
->
elp_work
,
wl1271_elp_work
);
INIT_DELAYED_WORK
(
&
wl
->
pspoll_work
,
wl1271_pspoll_work
);
INIT_WORK
(
&
wl
->
irq_work
,
wl1271_irq_work
);
...
...
@@ -3317,6 +3403,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl
->
bss_type
=
MAX_BSS_TYPE
;
wl
->
set_bss_type
=
MAX_BSS_TYPE
;
wl
->
fw_bss_type
=
MAX_BSS_TYPE
;
wl
->
last_tx_hlid
=
0
;
wl
->
ap_ps_map
=
0
;
wl
->
ap_fw_ps_map
=
0
;
memset
(
wl
->
tx_frames_map
,
0
,
sizeof
(
wl
->
tx_frames_map
));
for
(
i
=
0
;
i
<
ACX_TX_DESCRIPTORS
;
i
++
)
...
...
@@ -3412,5 +3501,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC
(
debug_level
,
"wl12xx debugging level"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Luciano Coelho <
luciano.coelho@nokia
.com>"
);
MODULE_AUTHOR
(
"Luciano Coelho <
coelho@ti
.com>"
);
MODULE_AUTHOR
(
"Juuso Oikarinen <juuso.oikarinen@nokia.com>"
);
drivers/net/wireless/wl12xx/ps.c
View file @
42774986
...
...
@@ -24,6 +24,7 @@
#include "reg.h"
#include "ps.h"
#include "io.h"
#include "tx.h"
#define WL1271_WAKEUP_TIMEOUT 500
...
...
@@ -173,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
return
ret
;
}
static
void
wl1271_ps_filter_frames
(
struct
wl1271
*
wl
,
u8
hlid
)
{
int
i
,
filtered
=
0
;
struct
sk_buff
*
skb
;
struct
ieee80211_tx_info
*
info
;
unsigned
long
flags
;
/* filter all frames currently the low level queus for this hlid */
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
{
while
((
skb
=
skb_dequeue
(
&
wl
->
links
[
hlid
].
tx_queue
[
i
])))
{
info
=
IEEE80211_SKB_CB
(
skb
);
info
->
flags
|=
IEEE80211_TX_STAT_TX_FILTERED
;
info
->
status
.
rates
[
0
].
idx
=
-
1
;
ieee80211_tx_status
(
wl
->
hw
,
skb
);
filtered
++
;
}
}
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
wl
->
tx_queue_count
-=
filtered
;
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
wl1271_handle_tx_low_watermark
(
wl
);
}
void
wl1271_ps_link_start
(
struct
wl1271
*
wl
,
u8
hlid
,
bool
clean_queues
)
{
struct
ieee80211_sta
*
sta
;
if
(
test_bit
(
hlid
,
&
wl
->
ap_ps_map
))
return
;
wl1271_debug
(
DEBUG_PSM
,
"start mac80211 PSM on hlid %d blks %d "
"clean_queues %d"
,
hlid
,
wl
->
links
[
hlid
].
allocated_blks
,
clean_queues
);
rcu_read_lock
();
sta
=
ieee80211_find_sta
(
wl
->
vif
,
wl
->
links
[
hlid
].
addr
);
if
(
!
sta
)
{
wl1271_error
(
"could not find sta %pM for starting ps"
,
wl
->
links
[
hlid
].
addr
);
rcu_read_unlock
();
return
;
}
ieee80211_sta_ps_transition_ni
(
sta
,
true
);
rcu_read_unlock
();
/* do we want to filter all frames from this link's queues? */
if
(
clean_queues
)
wl1271_ps_filter_frames
(
wl
,
hlid
);
__set_bit
(
hlid
,
&
wl
->
ap_ps_map
);
}
void
wl1271_ps_link_end
(
struct
wl1271
*
wl
,
u8
hlid
)
{
struct
ieee80211_sta
*
sta
;
if
(
!
test_bit
(
hlid
,
&
wl
->
ap_ps_map
))
return
;
wl1271_debug
(
DEBUG_PSM
,
"end mac80211 PSM on hlid %d"
,
hlid
);
__clear_bit
(
hlid
,
&
wl
->
ap_ps_map
);
rcu_read_lock
();
sta
=
ieee80211_find_sta
(
wl
->
vif
,
wl
->
links
[
hlid
].
addr
);
if
(
!
sta
)
{
wl1271_error
(
"could not find sta %pM for ending ps"
,
wl
->
links
[
hlid
].
addr
);
goto
end
;
}
ieee80211_sta_ps_transition_ni
(
sta
,
false
);
end:
rcu_read_unlock
();
}
drivers/net/wireless/wl12xx/ps.h
View file @
42774986
...
...
@@ -32,5 +32,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
void
wl1271_ps_elp_sleep
(
struct
wl1271
*
wl
);
int
wl1271_ps_elp_wakeup
(
struct
wl1271
*
wl
,
bool
chip_awake
);
void
wl1271_elp_work
(
struct
work_struct
*
work
);
void
wl1271_ps_link_start
(
struct
wl1271
*
wl
,
u8
hlid
,
bool
clean_queues
);
void
wl1271_ps_link_end
(
struct
wl1271
*
wl
,
u8
hlid
);
#endif
/* __WL1271_PS_H__ */
drivers/net/wireless/wl12xx/rx.c
View file @
42774986
...
...
@@ -92,7 +92,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
{
struct
wl1271_rx_descriptor
*
desc
;
struct
sk_buff
*
skb
;
u16
*
fc
;
struct
ieee80211_hdr
*
hdr
;
u8
*
buf
;
u8
beacon
=
0
;
...
...
@@ -118,8 +118,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length)
/* now we pull the descriptor out of the buffer */
skb_pull
(
skb
,
sizeof
(
*
desc
));
fc
=
(
u16
*
)
skb
->
data
;
if
(
(
*
fc
&
IEEE80211_FCTL_STYPE
)
==
IEEE80211_STYPE_BEACON
)
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
if
(
ieee80211_is_beacon
(
hdr
->
frame_control
)
)
beacon
=
1
;
wl1271_rx_status
(
wl
,
desc
,
IEEE80211_SKB_RXCB
(
skb
),
beacon
);
...
...
drivers/net/wireless/wl12xx/rx.h
View file @
42774986
...
...
@@ -30,10 +30,6 @@
#define WL1271_RX_MAX_RSSI -30
#define WL1271_RX_MIN_RSSI -95
#define WL1271_RX_ALIGN_TO 4
#define WL1271_RX_ALIGN(len) (((len) + WL1271_RX_ALIGN_TO - 1) & \
~(WL1271_RX_ALIGN_TO - 1))
#define SHORT_PREAMBLE_BIT BIT(0)
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
...
...
drivers/net/wireless/wl12xx/tx.c
View file @
42774986
...
...
@@ -70,8 +70,65 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
}
}
static
void
wl1271_tx_ap_update_inconnection_sta
(
struct
wl1271
*
wl
,
struct
sk_buff
*
skb
)
{
struct
ieee80211_hdr
*
hdr
;
/*
* add the station to the known list before transmitting the
* authentication response. this way it won't get de-authed by FW
* when transmitting too soon.
*/
hdr
=
(
struct
ieee80211_hdr
*
)(
skb
->
data
+
sizeof
(
struct
wl1271_tx_hw_descr
));
if
(
ieee80211_is_auth
(
hdr
->
frame_control
))
wl1271_acx_set_inconnection_sta
(
wl
,
hdr
->
addr1
);
}
static
void
wl1271_tx_regulate_link
(
struct
wl1271
*
wl
,
u8
hlid
)
{
bool
fw_ps
;
u8
tx_blks
;
/* only regulate station links */
if
(
hlid
<
WL1271_AP_STA_HLID_START
)
return
;
fw_ps
=
test_bit
(
hlid
,
(
unsigned
long
*
)
&
wl
->
ap_fw_ps_map
);
tx_blks
=
wl
->
links
[
hlid
].
allocated_blks
;
/*
* if in FW PS and there is enough data in FW we can put the link
* into high-level PS and clean out its TX queues.
*/
if
(
fw_ps
&&
tx_blks
>=
WL1271_PS_STA_MAX_BLOCKS
)
wl1271_ps_link_start
(
wl
,
hlid
,
true
);
}
u8
wl1271_tx_get_hlid
(
struct
sk_buff
*
skb
)
{
struct
ieee80211_tx_info
*
control
=
IEEE80211_SKB_CB
(
skb
);
if
(
control
->
control
.
sta
)
{
struct
wl1271_station
*
wl_sta
;
wl_sta
=
(
struct
wl1271_station
*
)
control
->
control
.
sta
->
drv_priv
;
return
wl_sta
->
hlid
;
}
else
{
struct
ieee80211_hdr
*
hdr
;
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
if
(
ieee80211_is_mgmt
(
hdr
->
frame_control
))
return
WL1271_AP_GLOBAL_HLID
;
else
return
WL1271_AP_BROADCAST_HLID
;
}
}
static
int
wl1271_tx_allocate
(
struct
wl1271
*
wl
,
struct
sk_buff
*
skb
,
u32
extra
,
u32
buf_offset
)
u32
buf_offset
,
u8
hlid
)
{
struct
wl1271_tx_hw_descr
*
desc
;
u32
total_len
=
skb
->
len
+
sizeof
(
struct
wl1271_tx_hw_descr
)
+
extra
;
...
...
@@ -100,6 +157,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
wl
->
tx_blocks_available
-=
total_blocks
;
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
wl
->
links
[
hlid
].
allocated_blks
+=
total_blocks
;
ret
=
0
;
wl1271_debug
(
DEBUG_TX
,
...
...
@@ -113,7 +173,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
}
static
void
wl1271_tx_fill_hdr
(
struct
wl1271
*
wl
,
struct
sk_buff
*
skb
,
u32
extra
,
struct
ieee80211_tx_info
*
control
)
u32
extra
,
struct
ieee80211_tx_info
*
control
,
u8
hlid
)
{
struct
timespec
ts
;
struct
wl1271_tx_hw_descr
*
desc
;
...
...
@@ -149,7 +210,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
desc
->
tid
=
ac
;
if
(
wl
->
bss_type
!=
BSS_TYPE_AP_BSS
)
{
desc
->
aid
=
TX_HW_DEFAULT_AID
;
desc
->
aid
=
hlid
;
/* if the packets are destined for AP (have a STA entry)
send them with AP rate policies, otherwise use default
...
...
@@ -159,25 +220,17 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
else
rate_idx
=
ACX_TX_BASIC_RATE
;
}
else
{
if
(
control
->
control
.
sta
)
{
struct
wl1271_station
*
wl_sta
;
wl_sta
=
(
struct
wl1271_station
*
)
control
->
control
.
sta
->
drv_priv
;
desc
->
hlid
=
wl_sta
->
hlid
;
desc
->
hlid
=
hlid
;
switch
(
hlid
)
{
case
WL1271_AP_GLOBAL_HLID
:
rate_idx
=
ACX_TX_AP_MODE_MGMT_RATE
;
break
;
case
WL1271_AP_BROADCAST_HLID
:
rate_idx
=
ACX_TX_AP_MODE_BCST_RATE
;
break
;
default:
rate_idx
=
ac
;
}
else
{
struct
ieee80211_hdr
*
hdr
;
hdr
=
(
struct
ieee80211_hdr
*
)
(
skb
->
data
+
sizeof
(
*
desc
));
if
(
ieee80211_is_mgmt
(
hdr
->
frame_control
))
{
desc
->
hlid
=
WL1271_AP_GLOBAL_HLID
;
rate_idx
=
ACX_TX_AP_MODE_MGMT_RATE
;
}
else
{
desc
->
hlid
=
WL1271_AP_BROADCAST_HLID
;
rate_idx
=
ACX_TX_AP_MODE_BCST_RATE
;
}
break
;
}
}
...
...
@@ -185,7 +238,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
desc
->
reserved
=
0
;
/* align the length (and store in terms of words) */
pad
=
WL1271_TX_ALIGN
(
skb
->
len
);
pad
=
ALIGN
(
skb
->
len
,
WL1271_TX_ALIGN_TO
);
desc
->
length
=
cpu_to_le16
(
pad
>>
2
);
/* calculate number of padding bytes */
...
...
@@ -208,6 +261,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
u32
extra
=
0
;
int
ret
=
0
;
u32
total_len
;
u8
hlid
;
if
(
!
skb
)
return
-
EINVAL
;
...
...
@@ -234,18 +288,28 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
}
}
ret
=
wl1271_tx_allocate
(
wl
,
skb
,
extra
,
buf_offset
);
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
hlid
=
wl1271_tx_get_hlid
(
skb
);
else
hlid
=
TX_HW_DEFAULT_AID
;
ret
=
wl1271_tx_allocate
(
wl
,
skb
,
extra
,
buf_offset
,
hlid
);
if
(
ret
<
0
)
return
ret
;
wl1271_tx_fill_hdr
(
wl
,
skb
,
extra
,
info
);
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
{
wl1271_tx_ap_update_inconnection_sta
(
wl
,
skb
);
wl1271_tx_regulate_link
(
wl
,
hlid
);
}
wl1271_tx_fill_hdr
(
wl
,
skb
,
extra
,
info
,
hlid
);
/*
* The length of each packet is stored in terms of words. Thus, we must
* pad the skb data to make sure its length is aligned.
* The number of padding bytes is computed and set in wl1271_tx_fill_hdr
*/
total_len
=
WL1271_TX_ALIGN
(
skb
->
len
);
total_len
=
ALIGN
(
skb
->
len
,
WL1271_TX_ALIGN_TO
);
memcpy
(
wl
->
aggr_buf
+
buf_offset
,
skb
->
data
,
skb
->
len
);
memset
(
wl
->
aggr_buf
+
buf_offset
+
skb
->
len
,
0
,
total_len
-
skb
->
len
);
...
...
@@ -279,7 +343,7 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
return
enabled_rates
;
}
static
void
handle_tx_low_watermark
(
struct
wl1271
*
wl
)
void
wl1271_
handle_tx_low_watermark
(
struct
wl1271
*
wl
)
{
unsigned
long
flags
;
...
...
@@ -293,7 +357,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl)
}
}
static
struct
sk_buff
*
wl1271_skb_dequeue
(
struct
wl1271
*
wl
)
static
struct
sk_buff
*
wl1271_s
ta_s
kb_dequeue
(
struct
wl1271
*
wl
)
{
struct
sk_buff
*
skb
=
NULL
;
unsigned
long
flags
;
...
...
@@ -319,12 +383,69 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
return
skb
;
}
static
struct
sk_buff
*
wl1271_ap_skb_dequeue
(
struct
wl1271
*
wl
)
{
struct
sk_buff
*
skb
=
NULL
;
unsigned
long
flags
;
int
i
,
h
,
start_hlid
;
/* start from the link after the last one */
start_hlid
=
(
wl
->
last_tx_hlid
+
1
)
%
AP_MAX_LINKS
;
/* dequeue according to AC, round robin on each link */
for
(
i
=
0
;
i
<
AP_MAX_LINKS
;
i
++
)
{
h
=
(
start_hlid
+
i
)
%
AP_MAX_LINKS
;
skb
=
skb_dequeue
(
&
wl
->
links
[
h
].
tx_queue
[
CONF_TX_AC_VO
]);
if
(
skb
)
goto
out
;
skb
=
skb_dequeue
(
&
wl
->
links
[
h
].
tx_queue
[
CONF_TX_AC_VI
]);
if
(
skb
)
goto
out
;
skb
=
skb_dequeue
(
&
wl
->
links
[
h
].
tx_queue
[
CONF_TX_AC_BE
]);
if
(
skb
)
goto
out
;
skb
=
skb_dequeue
(
&
wl
->
links
[
h
].
tx_queue
[
CONF_TX_AC_BK
]);
if
(
skb
)
goto
out
;
}
out:
if
(
skb
)
{
wl
->
last_tx_hlid
=
h
;
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
wl
->
tx_queue_count
--
;
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
}
else
{
wl
->
last_tx_hlid
=
0
;
}
return
skb
;
}
static
struct
sk_buff
*
wl1271_skb_dequeue
(
struct
wl1271
*
wl
)
{
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
return
wl1271_ap_skb_dequeue
(
wl
);
return
wl1271_sta_skb_dequeue
(
wl
);
}
static
void
wl1271_skb_queue_head
(
struct
wl1271
*
wl
,
struct
sk_buff
*
skb
)
{
unsigned
long
flags
;
int
q
=
wl1271_tx_get_queue
(
skb_get_queue_mapping
(
skb
));
skb_queue_head
(
&
wl
->
tx_queue
[
q
],
skb
);
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
{
u8
hlid
=
wl1271_tx_get_hlid
(
skb
);
skb_queue_head
(
&
wl
->
links
[
hlid
].
tx_queue
[
q
],
skb
);
/* make sure we dequeue the same packet next time */
wl
->
last_tx_hlid
=
(
hlid
+
AP_MAX_LINKS
-
1
)
%
AP_MAX_LINKS
;
}
else
{
skb_queue_head
(
&
wl
->
tx_queue
[
q
],
skb
);
}
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
wl
->
tx_queue_count
++
;
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
...
...
@@ -387,7 +508,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
if
(
sent_packets
)
{
/* interrupt the firmware with the new packets */
wl1271_write32
(
wl
,
WL1271_HOST_WR_ACCESS
,
wl
->
tx_packets_count
);
handle_tx_low_watermark
(
wl
);
wl1271_
handle_tx_low_watermark
(
wl
);
}
out:
...
...
@@ -504,32 +625,76 @@ void wl1271_tx_complete(struct wl1271 *wl)
}
}
void
wl1271_tx_reset_link_queues
(
struct
wl1271
*
wl
,
u8
hlid
)
{
struct
sk_buff
*
skb
;
int
i
,
total
=
0
;
unsigned
long
flags
;
struct
ieee80211_tx_info
*
info
;
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
{
while
((
skb
=
skb_dequeue
(
&
wl
->
links
[
hlid
].
tx_queue
[
i
])))
{
wl1271_debug
(
DEBUG_TX
,
"link freeing skb 0x%p"
,
skb
);
info
=
IEEE80211_SKB_CB
(
skb
);
info
->
status
.
rates
[
0
].
idx
=
-
1
;
info
->
status
.
rates
[
0
].
count
=
0
;
ieee80211_tx_status
(
wl
->
hw
,
skb
);
total
++
;
}
}
spin_lock_irqsave
(
&
wl
->
wl_lock
,
flags
);
wl
->
tx_queue_count
-=
total
;
spin_unlock_irqrestore
(
&
wl
->
wl_lock
,
flags
);
wl1271_handle_tx_low_watermark
(
wl
);
}
/* caller must hold wl->mutex */
void
wl1271_tx_reset
(
struct
wl1271
*
wl
)
{
int
i
;
struct
sk_buff
*
skb
;
struct
ieee80211_tx_info
*
info
;
/* TX failure */
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
{
while
((
skb
=
skb_dequeue
(
&
wl
->
tx_queue
[
i
])))
{
wl1271_debug
(
DEBUG_TX
,
"freeing skb 0x%p"
,
skb
);
ieee80211_tx_status
(
wl
->
hw
,
skb
);
if
(
wl
->
bss_type
==
BSS_TYPE_AP_BSS
)
{
for
(
i
=
0
;
i
<
AP_MAX_LINKS
;
i
++
)
{
wl1271_tx_reset_link_queues
(
wl
,
i
);
wl
->
links
[
i
].
allocated_blks
=
0
;
wl
->
links
[
i
].
prev_freed_blks
=
0
;
}
wl
->
last_tx_hlid
=
0
;
}
else
{
for
(
i
=
0
;
i
<
NUM_TX_QUEUES
;
i
++
)
{
while
((
skb
=
skb_dequeue
(
&
wl
->
tx_queue
[
i
])))
{
wl1271_debug
(
DEBUG_TX
,
"freeing skb 0x%p"
,
skb
);
info
=
IEEE80211_SKB_CB
(
skb
);
info
->
status
.
rates
[
0
].
idx
=
-
1
;
info
->
status
.
rates
[
0
].
count
=
0
;
ieee80211_tx_status
(
wl
->
hw
,
skb
);
}
}
}
wl
->
tx_queue_count
=
0
;
/*
* Make sure the driver is at a consistent state, in case this
* function is called from a context other than interface removal.
*/
handle_tx_low_watermark
(
wl
);
wl1271_
handle_tx_low_watermark
(
wl
);
for
(
i
=
0
;
i
<
ACX_TX_DESCRIPTORS
;
i
++
)
if
(
wl
->
tx_frames
[
i
]
!=
NULL
)
{
skb
=
wl
->
tx_frames
[
i
];
wl1271_free_tx_id
(
wl
,
i
);
wl1271_debug
(
DEBUG_TX
,
"freeing skb 0x%p"
,
skb
);
info
=
IEEE80211_SKB_CB
(
skb
);
info
->
status
.
rates
[
0
].
idx
=
-
1
;
info
->
status
.
rates
[
0
].
count
=
0
;
ieee80211_tx_status
(
wl
->
hw
,
skb
);
}
}
...
...
@@ -544,8 +709,8 @@ void wl1271_tx_flush(struct wl1271 *wl)
while
(
!
time_after
(
jiffies
,
timeout
))
{
mutex_lock
(
&
wl
->
mutex
);
wl1271_debug
(
DEBUG_TX
,
"flushing tx buffer: %d"
,
wl
->
tx_frames_cnt
);
wl1271_debug
(
DEBUG_TX
,
"flushing tx buffer: %d
%d
"
,
wl
->
tx_frames_cnt
,
wl
->
tx_queue_count
);
if
((
wl
->
tx_frames_cnt
==
0
)
&&
(
wl
->
tx_queue_count
==
0
))
{
mutex_unlock
(
&
wl
->
mutex
);
return
;
...
...
drivers/net/wireless/wl12xx/tx.h
View file @
42774986
...
...
@@ -53,8 +53,6 @@
#define TX_HW_RESULT_QUEUE_LEN_MASK 0xf
#define WL1271_TX_ALIGN_TO 4
#define WL1271_TX_ALIGN(len) (((len) + WL1271_TX_ALIGN_TO - 1) & \
~(WL1271_TX_ALIGN_TO - 1))
#define WL1271_TKIP_IV_SPACE 4
struct
wl1271_tx_hw_descr
{
...
...
@@ -152,5 +150,8 @@ void wl1271_tx_flush(struct wl1271 *wl);
u8
wl1271_rate_to_idx
(
int
rate
,
enum
ieee80211_band
band
);
u32
wl1271_tx_enabled_rates_get
(
struct
wl1271
*
wl
,
u32
rate_set
);
u32
wl1271_tx_min_rate_get
(
struct
wl1271
*
wl
);
u8
wl1271_tx_get_hlid
(
struct
sk_buff
*
skb
);
void
wl1271_tx_reset_link_queues
(
struct
wl1271
*
wl
,
u8
hlid
);
void
wl1271_handle_tx_low_watermark
(
struct
wl1271
*
wl
);
#endif
drivers/net/wireless/wl12xx/wl12xx.h
View file @
42774986
...
...
@@ -153,6 +153,17 @@ extern u32 wl12xx_debug_level;
#define WL1271_AP_BROADCAST_HLID 1
#define WL1271_AP_STA_HLID_START 2
/*
* When in AP-mode, we allow (at least) this number of mem-blocks
* to be transmitted to FW for a STA in PS-mode. Only when packets are
* present in the FW buffers it will wake the sleeping STA. We want to put
* enough packets for the driver to transmit all of its buffered data before
* the STA goes to sleep again. But we don't want to take too much mem-blocks
* as it might hurt the throughput of active STAs.
* The number of blocks (18) is enough for 2 large packets.
*/
#define WL1271_PS_STA_MAX_BLOCKS (2 * 9)
#define WL1271_AP_BSS_INDEX 0
#define WL1271_AP_DEF_INACTIV_SEC 300
#define WL1271_AP_DEF_BEACON_EXP 20
...
...
@@ -319,6 +330,17 @@ enum wl12xx_flags {
WL1271_FLAG_AP_STARTED
};
struct
wl1271_link
{
/* AP-mode - TX queue per AC in link */
struct
sk_buff_head
tx_queue
[
NUM_TX_QUEUES
];
/* accounting for allocated / available TX blocks in FW */
u8
allocated_blks
;
u8
prev_freed_blks
;
u8
addr
[
ETH_ALEN
];
};
struct
wl1271
{
struct
platform_device
*
plat_dev
;
struct
ieee80211_hw
*
hw
;
...
...
@@ -498,6 +520,21 @@ struct wl1271 {
/* RX BA constraint value */
bool
ba_support
;
u8
ba_rx_bitmap
;
/*
* AP-mode - links indexed by HLID. The global and broadcast links
* are always active.
*/
struct
wl1271_link
links
[
AP_MAX_LINKS
];
/* the hlid of the link where the last transmitted skb came from */
int
last_tx_hlid
;
/* AP-mode - a bitmap of links currently in PS mode according to FW */
u32
ap_fw_ps_map
;
/* AP-mode - a bitmap of links currently in PS mode in mac80211 */
unsigned
long
ap_ps_map
;
};
struct
wl1271_station
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment