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
nexedi
linux
Commits
75ae83d6
Commit
75ae83d6
authored
Oct 03, 2013
by
John W. Linville
Browse files
Options
Browse Files
Download
Plain Diff
Merge tag 'for-linville-20131001' of
git://github.com/kvalo/ath
parents
c21a7d66
6e712d42
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
984 additions
and
1015 deletions
+984
-1015
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/bmi.c
+27
-15
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.c
+26
-87
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/ce.h
+8
-62
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.c
+20
-4
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/core.h
+8
-14
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debug.c
+121
-0
drivers/net/wireless/ath/ath10k/debug.h
drivers/net/wireless/ath/ath10k/debug.h
+13
-1
drivers/net/wireless/ath/ath10k/htc.c
drivers/net/wireless/ath/ath10k/htc.c
+56
-177
drivers/net/wireless/ath/ath10k/htc.h
drivers/net/wireless/ath/ath10k/htc.h
+1
-4
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt.h
+7
-0
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_rx.c
+171
-151
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
+137
-150
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/hw.h
+5
-1
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.c
+120
-102
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.c
+52
-75
drivers/net/wireless/ath/ath10k/rx_desc.h
drivers/net/wireless/ath/ath10k/rx_desc.h
+22
-2
drivers/net/wireless/ath/ath10k/trace.h
drivers/net/wireless/ath/ath10k/trace.h
+28
-4
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/txrx.c
+22
-45
drivers/net/wireless/ath/ath10k/txrx.h
drivers/net/wireless/ath/ath10k/txrx.h
+2
-3
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.c
+94
-107
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath10k/wmi.h
+44
-11
No files found.
drivers/net/wireless/ath/ath10k/bmi.c
View file @
75ae83d6
...
...
@@ -22,7 +22,8 @@
void
ath10k_bmi_start
(
struct
ath10k
*
ar
)
{
ath10k_dbg
(
ATH10K_DBG_CORE
,
"BMI started
\n
"
);
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi start
\n
"
);
ar
->
bmi
.
done_sent
=
false
;
}
...
...
@@ -32,8 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar)
u32
cmdlen
=
sizeof
(
cmd
.
id
)
+
sizeof
(
cmd
.
done
);
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi done
\n
"
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_dbg
(
ATH10K_DBG_
CORE
,
"%s skipped
\n
"
,
__func__
);
ath10k_dbg
(
ATH10K_DBG_
BMI
,
"bmi skipped
\n
"
);
return
0
;
}
...
...
@@ -46,7 +49,6 @@ int ath10k_bmi_done(struct ath10k *ar)
return
ret
;
}
ath10k_dbg
(
ATH10K_DBG_CORE
,
"BMI done
\n
"
);
return
0
;
}
...
...
@@ -59,6 +61,8 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
u32
resplen
=
sizeof
(
resp
.
get_target_info
);
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi get target info
\n
"
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"BMI Get Target Info Command disallowed
\n
"
);
return
-
EBUSY
;
...
...
@@ -80,6 +84,7 @@ int ath10k_bmi_get_target_info(struct ath10k *ar,
target_info
->
version
=
__le32_to_cpu
(
resp
.
get_target_info
.
version
);
target_info
->
type
=
__le32_to_cpu
(
resp
.
get_target_info
.
type
);
return
0
;
}
...
...
@@ -92,15 +97,14 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
u32
rxlen
;
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi read address 0x%x length %d
\n
"
,
address
,
length
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"command disallowed
\n
"
);
return
-
EBUSY
;
}
ath10k_dbg
(
ATH10K_DBG_CORE
,
"%s: (device: 0x%p, address: 0x%x, length: %d)
\n
"
,
__func__
,
ar
,
address
,
length
);
while
(
length
)
{
rxlen
=
min_t
(
u32
,
length
,
BMI_MAX_DATA_SIZE
);
...
...
@@ -133,15 +137,14 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
u32
txlen
;
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi write address 0x%x length %d
\n
"
,
address
,
length
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"command disallowed
\n
"
);
return
-
EBUSY
;
}
ath10k_dbg
(
ATH10K_DBG_CORE
,
"%s: (device: 0x%p, address: 0x%x, length: %d)
\n
"
,
__func__
,
ar
,
address
,
length
);
while
(
length
)
{
txlen
=
min
(
length
,
BMI_MAX_DATA_SIZE
-
hdrlen
);
...
...
@@ -180,15 +183,14 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
u32
resplen
=
sizeof
(
resp
.
execute
);
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi execute address 0x%x param 0x%x
\n
"
,
address
,
*
param
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"command disallowed
\n
"
);
return
-
EBUSY
;
}
ath10k_dbg
(
ATH10K_DBG_CORE
,
"%s: (device: 0x%p, address: 0x%x, param: %d)
\n
"
,
__func__
,
ar
,
address
,
*
param
);
cmd
.
id
=
__cpu_to_le32
(
BMI_EXECUTE
);
cmd
.
execute
.
addr
=
__cpu_to_le32
(
address
);
cmd
.
execute
.
param
=
__cpu_to_le32
(
*
param
);
...
...
@@ -216,6 +218,9 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
u32
txlen
;
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi lz data buffer 0x%p length %d
\n
"
,
buffer
,
length
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"command disallowed
\n
"
);
return
-
EBUSY
;
...
...
@@ -250,6 +255,9 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
u32
cmdlen
=
sizeof
(
cmd
.
id
)
+
sizeof
(
cmd
.
lz_start
);
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi lz stream start address 0x%x
\n
"
,
address
);
if
(
ar
->
bmi
.
done_sent
)
{
ath10k_warn
(
"command disallowed
\n
"
);
return
-
EBUSY
;
...
...
@@ -275,6 +283,10 @@ int ath10k_bmi_fast_download(struct ath10k *ar,
u32
trailer_len
=
length
-
head_len
;
int
ret
;
ath10k_dbg
(
ATH10K_DBG_BMI
,
"bmi fast download address 0x%x buffer 0x%p length %d
\n
"
,
address
,
buffer
,
length
);
ret
=
ath10k_bmi_lz_stream_start
(
ar
,
address
);
if
(
ret
)
return
ret
;
...
...
drivers/net/wireless/ath/ath10k/ce.c
View file @
75ae83d6
...
...
@@ -338,33 +338,19 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
return
ret
;
}
void
ath10k_ce_sendlist_buf_add
(
struct
ce_sendlist
*
sendlist
,
u32
buffer
,
unsigned
int
nbytes
,
u32
flags
)
{
unsigned
int
num_items
=
sendlist
->
num_items
;
struct
ce_sendlist_item
*
item
;
item
=
&
sendlist
->
item
[
num_items
];
item
->
data
=
buffer
;
item
->
u
.
nbytes
=
nbytes
;
item
->
flags
=
flags
;
sendlist
->
num_items
++
;
}
int
ath10k_ce_sendlist_send
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
per_transfer_context
,
struct
ce_sendlist
*
sendlist
,
unsigned
int
transfer_id
)
unsigned
int
transfer_id
,
u32
paddr
,
unsigned
int
nbytes
,
u32
flags
)
{
struct
ath10k_ce_ring
*
src_ring
=
ce_state
->
src_ring
;
struct
ce_sendlist_item
*
item
;
struct
ath10k
*
ar
=
ce_state
->
ar
;
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
unsigned
int
nentries_mask
=
src_ring
->
nentries_mask
;
unsigned
int
num_items
=
sendlist
->
num_items
;
unsigned
int
sw_index
;
unsigned
int
write_index
;
int
i
,
delta
,
ret
=
-
ENOMEM
;
int
delta
,
ret
=
-
ENOMEM
;
spin_lock_bh
(
&
ar_pci
->
ce_lock
);
...
...
@@ -373,30 +359,12 @@ int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state,
delta
=
CE_RING_DELTA
(
nentries_mask
,
write_index
,
sw_index
-
1
);
if
(
delta
>=
num_items
)
{
/*
* Handle all but the last item uniformly.
*/
for
(
i
=
0
;
i
<
num_items
-
1
;
i
++
)
{
item
=
&
sendlist
->
item
[
i
];
ret
=
ath10k_ce_send_nolock
(
ce_state
,
CE_SENDLIST_ITEM_CTXT
,
(
u32
)
item
->
data
,
item
->
u
.
nbytes
,
transfer_id
,
item
->
flags
|
CE_SEND_FLAG_GATHER
);
if
(
ret
)
ath10k_warn
(
"CE send failed for item: %d
\n
"
,
i
);
}
/*
* Provide valid context pointer for final item.
*/
item
=
&
sendlist
->
item
[
i
];
if
(
delta
>=
1
)
{
ret
=
ath10k_ce_send_nolock
(
ce_state
,
per_transfer_context
,
(
u32
)
item
->
data
,
item
->
u
.
nbytes
,
transfer_id
,
item
->
flags
);
paddr
,
nbytes
,
transfer_id
,
flags
);
if
(
ret
)
ath10k_warn
(
"CE send failed
for last item: %d
\n
"
,
i
);
ath10k_warn
(
"CE send failed
: %d
\n
"
,
ret
);
}
spin_unlock_bh
(
&
ar_pci
->
ce_lock
);
...
...
@@ -742,11 +710,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
struct
ath10k_ce_pipe
*
ce_state
=
&
ar_pci
->
ce_states
[
ce_id
];
u32
ctrl_addr
=
ce_state
->
ctrl_addr
;
void
*
transfer_context
;
u32
buf
;
unsigned
int
nbytes
;
unsigned
int
id
;
unsigned
int
flags
;
int
ret
;
ret
=
ath10k_pci_wake
(
ar
);
...
...
@@ -759,38 +722,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
ath10k_ce_engine_int_status_clear
(
ar
,
ctrl_addr
,
HOST_IS_COPY_COMPLETE_MASK
);
if
(
ce_state
->
recv_cb
)
{
/*
* Pop completed recv buffers and call the registered
* recv callback for each
*/
while
(
ath10k_ce_completed_recv_next_nolock
(
ce_state
,
&
transfer_context
,
&
buf
,
&
nbytes
,
&
id
,
&
flags
)
==
0
)
{
spin_unlock_bh
(
&
ar_pci
->
ce_lock
);
ce_state
->
recv_cb
(
ce_state
,
transfer_context
,
buf
,
nbytes
,
id
,
flags
);
spin_lock_bh
(
&
ar_pci
->
ce_lock
);
}
}
spin_unlock_bh
(
&
ar_pci
->
ce_lock
);
if
(
ce_state
->
send_cb
)
{
/*
* Pop completed send buffers and call the registered
* send callback for each
*/
while
(
ath10k_ce_completed_send_next_nolock
(
ce_state
,
&
transfer_context
,
&
buf
,
&
nbytes
,
&
id
)
==
0
)
{
spin_unlock_bh
(
&
ar_pci
->
ce_lock
);
ce_state
->
send_cb
(
ce_state
,
transfer_context
,
buf
,
nbytes
,
id
);
spin_lock_bh
(
&
ar_pci
->
ce_lock
);
}
}
if
(
ce_state
->
recv_cb
)
ce_state
->
recv_cb
(
ce_state
);
if
(
ce_state
->
send_cb
)
ce_state
->
send_cb
(
ce_state
);
spin_lock_bh
(
&
ar_pci
->
ce_lock
);
/*
* Misc CE interrupts are not being handled, but still need
...
...
@@ -881,11 +821,7 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar)
}
void
ath10k_ce_send_cb_register
(
struct
ath10k_ce_pipe
*
ce_state
,
void
(
*
send_cb
)(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
),
void
(
*
send_cb
)(
struct
ath10k_ce_pipe
*
),
int
disable_interrupts
)
{
struct
ath10k
*
ar
=
ce_state
->
ar
;
...
...
@@ -898,12 +834,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
}
void
ath10k_ce_recv_cb_register
(
struct
ath10k_ce_pipe
*
ce_state
,
void
(
*
recv_cb
)(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
,
unsigned
int
flags
))
void
(
*
recv_cb
)(
struct
ath10k_ce_pipe
*
))
{
struct
ath10k
*
ar
=
ce_state
->
ar
;
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
...
...
@@ -1010,6 +941,10 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
ath10k_ce_src_ring_lowmark_set
(
ar
,
ctrl_addr
,
0
);
ath10k_ce_src_ring_highmark_set
(
ar
,
ctrl_addr
,
nentries
);
ath10k_dbg
(
ATH10K_DBG_BOOT
,
"boot ce src ring id %d entries %d base_addr %p
\n
"
,
ce_id
,
nentries
,
src_ring
->
base_addr_owner_space
);
return
0
;
}
...
...
@@ -1091,6 +1026,10 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
ath10k_ce_dest_ring_lowmark_set
(
ar
,
ctrl_addr
,
0
);
ath10k_ce_dest_ring_highmark_set
(
ar
,
ctrl_addr
,
nentries
);
ath10k_dbg
(
ATH10K_DBG_BOOT
,
"boot ce dest ring id %d entries %d base_addr %p
\n
"
,
ce_id
,
nentries
,
dest_ring
->
base_addr_owner_space
);
return
0
;
}
...
...
drivers/net/wireless/ath/ath10k/ce.h
View file @
75ae83d6
...
...
@@ -27,7 +27,6 @@
/* Descriptor rings must be aligned to this boundary */
#define CE_DESC_RING_ALIGN 8
#define CE_SENDLIST_ITEMS_MAX 12
#define CE_SEND_FLAG_GATHER 0x00010000
/*
...
...
@@ -116,41 +115,14 @@ struct ath10k_ce_pipe {
u32
ctrl_addr
;
void
(
*
send_cb
)
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
per_transfer_send_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
);
void
(
*
recv_cb
)
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
per_transfer_recv_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
,
unsigned
int
flags
);
void
(
*
send_cb
)(
struct
ath10k_ce_pipe
*
);
void
(
*
recv_cb
)(
struct
ath10k_ce_pipe
*
);
unsigned
int
src_sz_max
;
struct
ath10k_ce_ring
*
src_ring
;
struct
ath10k_ce_ring
*
dest_ring
;
};
struct
ce_sendlist_item
{
/* e.g. buffer or desc list */
dma_addr_t
data
;
union
{
/* simple buffer */
unsigned
int
nbytes
;
/* Rx descriptor list */
unsigned
int
ndesc
;
}
u
;
/* externally-specified flags; OR-ed with internal flags */
u32
flags
;
};
struct
ce_sendlist
{
unsigned
int
num_items
;
struct
ce_sendlist_item
item
[
CE_SENDLIST_ITEMS_MAX
];
};
/* Copy Engine settable attributes */
struct
ce_attr
;
...
...
@@ -181,20 +153,9 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned
int
flags
);
void
ath10k_ce_send_cb_register
(
struct
ath10k_ce_pipe
*
ce_state
,
void
(
*
send_cb
)(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
),
void
(
*
send_cb
)(
struct
ath10k_ce_pipe
*
),
int
disable_interrupts
);
/* Append a simple buffer (address/length) to a sendlist. */
void
ath10k_ce_sendlist_buf_add
(
struct
ce_sendlist
*
sendlist
,
u32
buffer
,
unsigned
int
nbytes
,
/* OR-ed with internal flags */
u32
flags
);
/*
* Queue a "sendlist" of buffers to be sent using gather to a single
* anonymous destination buffer
...
...
@@ -206,10 +167,10 @@ void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
* Implemenation note: Pushes multiple buffers with Gather to Source ring.
*/
int
ath10k_ce_sendlist_send
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
per_transfer_
send_
context
,
struct
ce_sendlist
*
sendlist
,
/* 14 bits */
u
nsigned
int
transfer_id
);
void
*
per_transfer_context
,
unsigned
int
transfer_id
,
u32
paddr
,
unsigned
int
nbytes
,
u
32
flags
);
/*==================Recv=======================*/
...
...
@@ -228,12 +189,7 @@ int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
u32
buffer
);
void
ath10k_ce_recv_cb_register
(
struct
ath10k_ce_pipe
*
ce_state
,
void
(
*
recv_cb
)(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
buffer
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
,
unsigned
int
flags
));
void
(
*
recv_cb
)(
struct
ath10k_ce_pipe
*
));
/* recv flags */
/* Data is byte-swapped */
...
...
@@ -325,16 +281,6 @@ struct ce_attr {
unsigned
int
dest_nentries
;
};
/*
* When using sendlist_send to transfer multiple buffer fragments, the
* transfer context of each fragment, except last one, will be filled
* with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
* each fragment done with send and the transfer context would be
* CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
* status of a send completion.
*/
#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef)
#define SR_BA_ADDRESS 0x0000
#define SR_SIZE_ADDRESS 0x0004
#define DR_BA_ADDRESS 0x0008
...
...
drivers/net/wireless/ath/ath10k/core.c
View file @
75ae83d6
...
...
@@ -53,7 +53,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
static
void
ath10k_send_suspend_complete
(
struct
ath10k
*
ar
)
{
ath10k_dbg
(
ATH10K_DBG_
CORE
,
"%s
\n
"
,
__func__
);
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"boot suspend complete
\n
"
);
ar
->
is_target_paused
=
true
;
wake_up
(
&
ar
->
event_queue
);
...
...
@@ -101,7 +101,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
goto
timeout
;
}
ath10k_dbg
(
ATH10K_DBG_
CORE
,
"core
wmi ready
\n
"
);
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"boot
wmi ready
\n
"
);
return
0
;
timeout:
...
...
@@ -203,8 +203,8 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
return
ret
;
}
ath10k_dbg
(
ATH10K_DBG_
CORE
,
"
ath10k: Board extended Data download addr:
0x%x
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"
boot push board extended data addr
0x%x
\n
"
,
board_ext_data_addr
);
if
(
board_ext_data_addr
==
0
)
...
...
@@ -435,6 +435,13 @@ static int ath10k_init_uart(struct ath10k *ar)
return
ret
;
}
/* Set the UART baud rate to 19200. */
ret
=
ath10k_bmi_write32
(
ar
,
hi_desired_baud_rate
,
19200
);
if
(
ret
)
{
ath10k_warn
(
"could not set the baud rate (%d)
\n
"
,
ret
);
return
ret
;
}
ath10k_info
(
"UART prints enabled
\n
"
);
return
0
;
}
...
...
@@ -630,6 +637,10 @@ int ath10k_core_start(struct ath10k *ar)
if
(
status
)
goto
err_disconnect_htc
;
status
=
ath10k_debug_start
(
ar
);
if
(
status
)
goto
err_disconnect_htc
;
ar
->
free_vdev_map
=
(
1
<<
TARGET_NUM_VDEVS
)
-
1
;
return
0
;
...
...
@@ -647,6 +658,7 @@ EXPORT_SYMBOL(ath10k_core_start);
void
ath10k_core_stop
(
struct
ath10k
*
ar
)
{
ath10k_debug_stop
(
ar
);
ath10k_htc_stop
(
&
ar
->
htc
);
ath10k_htt_detach
(
&
ar
->
htt
);
ath10k_wmi_detach
(
ar
);
...
...
@@ -710,6 +722,9 @@ static int ath10k_core_check_chip_id(struct ath10k *ar)
{
u32
hw_revision
=
MS
(
ar
->
chip_id
,
SOC_CHIP_ID_REV
);
ath10k_dbg
(
ATH10K_DBG_BOOT
,
"boot chip_id 0x%08x hw_revision 0x%x
\n
"
,
ar
->
chip_id
,
hw_revision
);
/* Check that we are not using hw1.0 (some of them have same pci id
* as hw2.0) before doing anything else as ath10k crashes horribly
* due to missing hw1.0 workarounds. */
...
...
@@ -777,6 +792,7 @@ void ath10k_core_unregister(struct ath10k *ar)
* Otherwise we will fail to submit commands to FW and mac80211 will be
* unhappy about callback failures. */
ath10k_mac_unregister
(
ar
);
ath10k_core_free_firmware_files
(
ar
);
}
EXPORT_SYMBOL
(
ath10k_core_unregister
);
...
...
drivers/net/wireless/ath/ath10k/core.h
View file @
75ae83d6
...
...
@@ -52,18 +52,12 @@ struct ath10k_skb_cb {
struct
{
u8
vdev_id
;
u16
msdu_id
;
u8
tid
;
bool
is_offchan
;
bool
is_conf
;
bool
discard
;
bool
no_ack
;
u8
refcount
;
struct
sk_buff
*
txfrag
;
struct
sk_buff
*
msdu
;
}
__packed
htt
;
/* 4 bytes left on 64bit arch */
u8
frag_len
;
u8
pad_len
;
}
__packed
htt
;
}
__packed
;
static
inline
struct
ath10k_skb_cb
*
ATH10K_SKB_CB
(
struct
sk_buff
*
skb
)
...
...
@@ -112,11 +106,7 @@ struct ath10k_wmi {
enum
ath10k_htc_ep_id
eid
;
struct
completion
service_ready
;
struct
completion
unified_ready
;
atomic_t
pending_tx_count
;
wait_queue_head_t
wq
;
struct
sk_buff_head
wmi_event_list
;
struct
work_struct
wmi_event_work
;
wait_queue_head_t
tx_credits_wq
;
};
struct
ath10k_peer_stat
{
...
...
@@ -203,6 +193,7 @@ struct ath10k_vif {
enum
wmi_vdev_subtype
vdev_subtype
;
u32
beacon_interval
;
u32
dtim_period
;
struct
sk_buff
*
beacon
;
struct
ath10k
*
ar
;
struct
ieee80211_vif
*
vif
;
...
...
@@ -246,6 +237,9 @@ struct ath10k_debug {
u32
wmi_service_bitmap
[
WMI_SERVICE_BM_SIZE
];
struct
completion
event_stats_compl
;
unsigned
long
htt_stats_mask
;
struct
delayed_work
htt_stats_dwork
;
};
enum
ath10k_state
{
...
...
drivers/net/wireless/ath/ath10k/debug.c
View file @
75ae83d6
...
...
@@ -21,6 +21,9 @@
#include "core.h"
#include "debug.h"
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
static
int
ath10k_printk
(
const
char
*
level
,
const
char
*
fmt
,
...)
{
struct
va_format
vaf
;
...
...
@@ -517,6 +520,117 @@ static const struct file_operations fops_chip_id = {
.
llseek
=
default_llseek
,
};
static
int
ath10k_debug_htt_stats_req
(
struct
ath10k
*
ar
)
{
u64
cookie
;
int
ret
;
lockdep_assert_held
(
&
ar
->
conf_mutex
);
if
(
ar
->
debug
.
htt_stats_mask
==
0
)
/* htt stats are disabled */
return
0
;
if
(
ar
->
state
!=
ATH10K_STATE_ON
)
return
0
;
cookie
=
get_jiffies_64
();
ret
=
ath10k_htt_h2t_stats_req
(
&
ar
->
htt
,
ar
->
debug
.
htt_stats_mask
,
cookie
);
if
(
ret
)
{
ath10k_warn
(
"failed to send htt stats request: %d
\n
"
,
ret
);
return
ret
;
}
queue_delayed_work
(
ar
->
workqueue
,
&
ar
->
debug
.
htt_stats_dwork
,
msecs_to_jiffies
(
ATH10K_DEBUG_HTT_STATS_INTERVAL
));
return
0
;
}
static
void
ath10k_debug_htt_stats_dwork
(
struct
work_struct
*
work
)
{
struct
ath10k
*
ar
=
container_of
(
work
,
struct
ath10k
,
debug
.
htt_stats_dwork
.
work
);
mutex_lock
(
&
ar
->
conf_mutex
);
ath10k_debug_htt_stats_req
(
ar
);
mutex_unlock
(
&
ar
->
conf_mutex
);
}
static
ssize_t
ath10k_read_htt_stats_mask
(
struct
file
*
file
,
char
__user
*
user_buf
,
size_t
count
,
loff_t
*
ppos
)
{
struct
ath10k
*
ar
=
file
->
private_data
;
char
buf
[
32
];
unsigned
int
len
;
len
=
scnprintf
(
buf
,
sizeof
(
buf
),
"%lu
\n
"
,
ar
->
debug
.
htt_stats_mask
);
return
simple_read_from_buffer
(
user_buf
,
count
,
ppos
,
buf
,
len
);
}
static
ssize_t
ath10k_write_htt_stats_mask
(
struct
file
*
file
,
const
char
__user
*
user_buf
,
size_t
count
,
loff_t
*
ppos
)
{
struct
ath10k
*
ar
=
file
->
private_data
;
unsigned
long
mask
;
int
ret
;
ret
=
kstrtoul_from_user
(
user_buf
,
count
,
0
,
&
mask
);
if
(
ret
)
return
ret
;
/* max 8 bit masks (for now) */
if
(
mask
>
0xff
)
return
-
E2BIG
;
mutex_lock
(
&
ar
->
conf_mutex
);
ar
->
debug
.
htt_stats_mask
=
mask
;
ret
=
ath10k_debug_htt_stats_req
(
ar
);
if
(
ret
)
goto
out
;
ret
=
count
;
out:
mutex_unlock
(
&
ar
->
conf_mutex
);
return
ret
;
}
static
const
struct
file_operations
fops_htt_stats_mask
=
{
.
read
=
ath10k_read_htt_stats_mask
,
.
write
=
ath10k_write_htt_stats_mask
,
.
open
=
simple_open
,
.
owner
=
THIS_MODULE
,
.
llseek
=
default_llseek
,
};
int
ath10k_debug_start
(
struct
ath10k
*
ar
)
{
int
ret
;
ret
=
ath10k_debug_htt_stats_req
(
ar
);
if
(
ret
)
/* continue normally anyway, this isn't serious */
ath10k_warn
(
"failed to start htt stats workqueue: %d
\n
"
,
ret
);
return
0
;
}
void
ath10k_debug_stop
(
struct
ath10k
*
ar
)
{
cancel_delayed_work_sync
(
&
ar
->
debug
.
htt_stats_dwork
);
}
int
ath10k_debug_create
(
struct
ath10k
*
ar
)
{
ar
->
debug
.
debugfs_phy
=
debugfs_create_dir
(
"ath10k"
,
...
...
@@ -525,6 +639,9 @@ int ath10k_debug_create(struct ath10k *ar)
if
(
!
ar
->
debug
.
debugfs_phy
)
return
-
ENOMEM
;
INIT_DELAYED_WORK
(
&
ar
->
debug
.
htt_stats_dwork
,
ath10k_debug_htt_stats_dwork
);
init_completion
(
&
ar
->
debug
.
event_stats_compl
);
debugfs_create_file
(
"fw_stats"
,
S_IRUSR
,
ar
->
debug
.
debugfs_phy
,
ar
,
...
...
@@ -539,8 +656,12 @@ int ath10k_debug_create(struct ath10k *ar)
debugfs_create_file
(
"chip_id"
,
S_IRUSR
,
ar
->
debug
.
debugfs_phy
,
ar
,
&
fops_chip_id
);
debugfs_create_file
(
"htt_stats_mask"
,
S_IRUSR
,
ar
->
debug
.
debugfs_phy
,
ar
,
&
fops_htt_stats_mask
);
return
0
;
}
#endif
/* CONFIG_ATH10K_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
...
...
drivers/net/wireless/ath/ath10k/debug.h
View file @
75ae83d6
...
...
@@ -27,11 +27,12 @@ enum ath10k_debug_mask {
ATH10K_DBG_HTC
=
0x00000004
,
ATH10K_DBG_HTT
=
0x00000008
,
ATH10K_DBG_MAC
=
0x00000010
,
ATH10K_DBG_
CORE
=
0x00000020
,
ATH10K_DBG_
BOOT
=
0x00000020
,
ATH10K_DBG_PCI_DUMP
=
0x00000040
,
ATH10K_DBG_HTT_DUMP
=
0x00000080
,
ATH10K_DBG_MGMT
=
0x00000100
,
ATH10K_DBG_DATA
=
0x00000200
,
ATH10K_DBG_BMI
=
0x00000400
,
ATH10K_DBG_ANY
=
0xffffffff
,
};
...
...
@@ -42,6 +43,8 @@ extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
extern
__printf
(
1
,
2
)
int
ath10k_warn
(
const
char
*
fmt
,
...);
#ifdef CONFIG_ATH10K_DEBUGFS
int
ath10k_debug_start
(
struct
ath10k
*
ar
);
void
ath10k_debug_stop
(
struct
ath10k
*
ar
);
int
ath10k_debug_create
(
struct
ath10k
*
ar
);
void
ath10k_debug_read_service_map
(
struct
ath10k
*
ar
,
void
*
service_map
,
...
...
@@ -50,6 +53,15 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
struct
wmi_stats_event
*
ev
);
#else
static
inline
int
ath10k_debug_start
(
struct
ath10k
*
ar
)
{
return
0
;
}
static
inline
void
ath10k_debug_stop
(
struct
ath10k
*
ar
)
{
}
static
inline
int
ath10k_debug_create
(
struct
ath10k
*
ar
)
{
return
0
;
...
...
drivers/net/wireless/ath/ath10k/htc.c
View file @
75ae83d6
...
...
@@ -103,10 +103,10 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
struct
ath10k_htc_hdr
*
hdr
;
hdr
=
(
struct
ath10k_htc_hdr
*
)
skb
->
data
;
memset
(
hdr
,
0
,
sizeof
(
*
hdr
));
hdr
->
eid
=
ep
->
eid
;
hdr
->
len
=
__cpu_to_le16
(
skb
->
len
-
sizeof
(
*
hdr
));
hdr
->
flags
=
0
;
spin_lock_bh
(
&
ep
->
htc
->
tx_lock
);
hdr
->
seq_no
=
ep
->
seq_no
++
;
...
...
@@ -117,134 +117,13 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
spin_unlock_bh
(
&
ep
->
htc
->
tx_lock
);
}
static
int
ath10k_htc_issue_skb
(
struct
ath10k_htc
*
htc
,
struct
ath10k_htc_ep
*
ep
,
struct
sk_buff
*
skb
,
u8
credits
)
{
struct
ath10k_skb_cb
*
skb_cb
=
ATH10K_SKB_CB
(
skb
);
int
ret
;
ath10k_dbg
(
ATH10K_DBG_HTC
,
"%s: ep %d skb %p
\n
"
,
__func__
,
ep
->
eid
,
skb
);
ath10k_htc_prepare_tx_skb
(
ep
,
skb
);
ret
=
ath10k_skb_map
(
htc
->
ar
->
dev
,
skb
);
if
(
ret
)
goto
err
;
ret
=
ath10k_hif_send_head
(
htc
->
ar
,
ep
->
ul_pipe_id
,
ep
->
eid
,
skb
->
len
,
skb
);
if
(
unlikely
(
ret
))
goto
err
;
return
0
;
err:
ath10k_warn
(
"HTC issue failed: %d
\n
"
,
ret
);
spin_lock_bh
(
&
htc
->
tx_lock
);
ep
->
tx_credits
+=
credits
;
spin_unlock_bh
(
&
htc
->
tx_lock
);
/* this is the simplest way to handle out-of-resources for non-credit
* based endpoints. credit based endpoints can still get -ENOSR, but
* this is highly unlikely as credit reservation should prevent that */
if
(
ret
==
-
ENOSR
)
{
spin_lock_bh
(
&
htc
->
tx_lock
);
__skb_queue_head
(
&
ep
->
tx_queue
,
skb
);
spin_unlock_bh
(
&
htc
->
tx_lock
);
return
ret
;
}
skb_cb
->
is_aborted
=
true
;
ath10k_htc_notify_tx_completion
(
ep
,
skb
);
return
ret
;
}
static
struct
sk_buff
*
ath10k_htc_get_skb_credit_based
(
struct
ath10k_htc
*
htc
,
struct
ath10k_htc_ep
*
ep
,
u8
*
credits
)
{
struct
sk_buff
*
skb
;
struct
ath10k_skb_cb
*
skb_cb
;
int
credits_required
;
int
remainder
;
unsigned
int
transfer_len
;
lockdep_assert_held
(
&
htc
->
tx_lock
);
skb
=
__skb_dequeue
(
&
ep
->
tx_queue
);
if
(
!
skb
)
return
NULL
;
skb_cb
=
ATH10K_SKB_CB
(
skb
);
transfer_len
=
skb
->
len
;
if
(
likely
(
transfer_len
<=
htc
->
target_credit_size
))
{
credits_required
=
1
;
}
else
{
/* figure out how many credits this message requires */
credits_required
=
transfer_len
/
htc
->
target_credit_size
;
remainder
=
transfer_len
%
htc
->
target_credit_size
;
if
(
remainder
)
credits_required
++
;
}
ath10k_dbg
(
ATH10K_DBG_HTC
,
"Credits required %d got %d
\n
"
,
credits_required
,
ep
->
tx_credits
);
if
(
ep
->
tx_credits
<
credits_required
)
{
__skb_queue_head
(
&
ep
->
tx_queue
,
skb
);
return
NULL
;
}
ep
->
tx_credits
-=
credits_required
;
*
credits
=
credits_required
;
return
skb
;
}
static
void
ath10k_htc_send_work
(
struct
work_struct
*
work
)
{
struct
ath10k_htc_ep
*
ep
=
container_of
(
work
,
struct
ath10k_htc_ep
,
send_work
);
struct
ath10k_htc
*
htc
=
ep
->
htc
;
struct
sk_buff
*
skb
;
u8
credits
=
0
;
int
ret
;
while
(
true
)
{
if
(
ep
->
ul_is_polled
)
ath10k_htc_send_complete_check
(
ep
,
0
);
spin_lock_bh
(
&
htc
->
tx_lock
);
if
(
ep
->
tx_credit_flow_enabled
)
skb
=
ath10k_htc_get_skb_credit_based
(
htc
,
ep
,
&
credits
);
else
skb
=
__skb_dequeue
(
&
ep
->
tx_queue
);
spin_unlock_bh
(
&
htc
->
tx_lock
);
if
(
!
skb
)
break
;
ret
=
ath10k_htc_issue_skb
(
htc
,
ep
,
skb
,
credits
);
if
(
ret
==
-
ENOSR
)
break
;
}
}
int
ath10k_htc_send
(
struct
ath10k_htc
*
htc
,
enum
ath10k_htc_ep_id
eid
,
struct
sk_buff
*
skb
)
{
struct
ath10k_htc_ep
*
ep
=
&
htc
->
endpoint
[
eid
];
int
credits
=
0
;
int
ret
;
if
(
htc
->
ar
->
state
==
ATH10K_STATE_WEDGED
)
return
-
ECOMM
;
...
...
@@ -254,18 +133,55 @@ int ath10k_htc_send(struct ath10k_htc *htc,
return
-
ENOENT
;
}
/* FIXME: This looks ugly, can we fix it? */
spin_lock_bh
(
&
htc
->
tx_lock
);
if
(
htc
->
stopped
)
{
spin_unlock_bh
(
&
htc
->
tx_lock
);
return
-
ESHUTDOWN
;
}
spin_unlock_bh
(
&
htc
->
tx_lock
);
__skb_queue_tail
(
&
ep
->
tx_queue
,
skb
);
skb_push
(
skb
,
sizeof
(
struct
ath10k_htc_hdr
));
spin_unlock_bh
(
&
htc
->
tx_lock
);
queue_work
(
htc
->
ar
->
workqueue
,
&
ep
->
send_work
);
if
(
ep
->
tx_credit_flow_enabled
)
{
credits
=
DIV_ROUND_UP
(
skb
->
len
,
htc
->
target_credit_size
);
spin_lock_bh
(
&
htc
->
tx_lock
);
if
(
ep
->
tx_credits
<
credits
)
{
spin_unlock_bh
(
&
htc
->
tx_lock
);
ret
=
-
EAGAIN
;
goto
err_pull
;
}
ep
->
tx_credits
-=
credits
;
spin_unlock_bh
(
&
htc
->
tx_lock
);
}
ath10k_htc_prepare_tx_skb
(
ep
,
skb
);
ret
=
ath10k_skb_map
(
htc
->
ar
->
dev
,
skb
);
if
(
ret
)
goto
err_credits
;
ret
=
ath10k_hif_send_head
(
htc
->
ar
,
ep
->
ul_pipe_id
,
ep
->
eid
,
skb
->
len
,
skb
);
if
(
ret
)
goto
err_unmap
;
return
0
;
err_unmap:
ath10k_skb_unmap
(
htc
->
ar
->
dev
,
skb
);
err_credits:
if
(
ep
->
tx_credit_flow_enabled
)
{
spin_lock_bh
(
&
htc
->
tx_lock
);
ep
->
tx_credits
+=
credits
;
spin_unlock_bh
(
&
htc
->
tx_lock
);
if
(
ep
->
ep_ops
.
ep_tx_credits
)
ep
->
ep_ops
.
ep_tx_credits
(
htc
->
ar
);
}
err_pull:
skb_pull
(
skb
,
sizeof
(
struct
ath10k_htc_hdr
));
return
ret
;
}
static
int
ath10k_htc_tx_completion_handler
(
struct
ath10k
*
ar
,
...
...
@@ -278,39 +194,9 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
ath10k_htc_notify_tx_completion
(
ep
,
skb
);
/* the skb now belongs to the completion handler */
/* note: when using TX credit flow, the re-checking of queues happens
* when credits flow back from the target. in the non-TX credit case,
* we recheck after the packet completes */
spin_lock_bh
(
&
htc
->
tx_lock
);
if
(
!
ep
->
tx_credit_flow_enabled
&&
!
htc
->
stopped
)
queue_work
(
ar
->
workqueue
,
&
ep
->
send_work
);
spin_unlock_bh
(
&
htc
->
tx_lock
);
return
0
;
}
/* flush endpoint TX queue */
static
void
ath10k_htc_flush_endpoint_tx
(
struct
ath10k_htc
*
htc
,
struct
ath10k_htc_ep
*
ep
)
{
struct
sk_buff
*
skb
;
struct
ath10k_skb_cb
*
skb_cb
;
spin_lock_bh
(
&
htc
->
tx_lock
);
for
(;;)
{
skb
=
__skb_dequeue
(
&
ep
->
tx_queue
);
if
(
!
skb
)
break
;
skb_cb
=
ATH10K_SKB_CB
(
skb
);
skb_cb
->
is_aborted
=
true
;
ath10k_htc_notify_tx_completion
(
ep
,
skb
);
}
spin_unlock_bh
(
&
htc
->
tx_lock
);
cancel_work_sync
(
&
ep
->
send_work
);
}
/***********/
/* Receive */
/***********/
...
...
@@ -340,8 +226,11 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
ep
=
&
htc
->
endpoint
[
report
->
eid
];
ep
->
tx_credits
+=
report
->
credits
;
if
(
ep
->
tx_credits
&&
!
skb_queue_empty
(
&
ep
->
tx_queue
))
queue_work
(
htc
->
ar
->
workqueue
,
&
ep
->
send_work
);
if
(
ep
->
ep_ops
.
ep_tx_credits
)
{
spin_unlock_bh
(
&
htc
->
tx_lock
);
ep
->
ep_ops
.
ep_tx_credits
(
htc
->
ar
);
spin_lock_bh
(
&
htc
->
tx_lock
);
}
}
spin_unlock_bh
(
&
htc
->
tx_lock
);
}
...
...
@@ -599,10 +488,8 @@ static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
ep
->
max_ep_message_len
=
0
;
ep
->
max_tx_queue_depth
=
0
;
ep
->
eid
=
i
;
skb_queue_head_init
(
&
ep
->
tx_queue
);
ep
->
htc
=
htc
;
ep
->
tx_credit_flow_enabled
=
true
;
INIT_WORK
(
&
ep
->
send_work
,
ath10k_htc_send_work
);
}
}
...
...
@@ -752,8 +639,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
tx_alloc
=
ath10k_htc_get_credit_allocation
(
htc
,
conn_req
->
service_id
);
if
(
!
tx_alloc
)
ath10k_dbg
(
ATH10K_DBG_
HTC
,
"
HTC S
ervice %s does not allocate target credits
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"
boot htc s
ervice %s does not allocate target credits
\n
"
,
htc_service_name
(
conn_req
->
service_id
));
skb
=
ath10k_htc_build_tx_ctrl_skb
(
htc
->
ar
);
...
...
@@ -873,19 +760,19 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
if
(
status
)
return
status
;
ath10k_dbg
(
ATH10K_DBG_
HTC
,
"
HTC service: %s UL pipe: %d DL pipe: %d eid:
%d ready
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"
boot htc service '%s' ul pipe %d dl pipe %d eid
%d ready
\n
"
,
htc_service_name
(
ep
->
service_id
),
ep
->
ul_pipe_id
,
ep
->
dl_pipe_id
,
ep
->
eid
);
ath10k_dbg
(
ATH10K_DBG_
HTC
,
"
EP %d UL polled: %d, DL polled:
%d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"
boot htc ep %d ul polled %d dl polled
%d
\n
"
,
ep
->
eid
,
ep
->
ul_is_polled
,
ep
->
dl_is_polled
);
if
(
disable_credit_flow_ctrl
&&
ep
->
tx_credit_flow_enabled
)
{
ep
->
tx_credit_flow_enabled
=
false
;
ath10k_dbg
(
ATH10K_DBG_
HTC
,
"
HTC service: %s eid:
%d TX flow control disabled
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"
boot htc service '%s' eid
%d TX flow control disabled
\n
"
,
htc_service_name
(
ep
->
service_id
),
assigned_eid
);
}
...
...
@@ -945,18 +832,10 @@ int ath10k_htc_start(struct ath10k_htc *htc)
*/
void
ath10k_htc_stop
(
struct
ath10k_htc
*
htc
)
{
int
i
;
struct
ath10k_htc_ep
*
ep
;
spin_lock_bh
(
&
htc
->
tx_lock
);
htc
->
stopped
=
true
;
spin_unlock_bh
(
&
htc
->
tx_lock
);
for
(
i
=
ATH10K_HTC_EP_0
;
i
<
ATH10K_HTC_EP_COUNT
;
i
++
)
{
ep
=
&
htc
->
endpoint
[
i
];
ath10k_htc_flush_endpoint_tx
(
htc
,
ep
);
}
ath10k_hif_stop
(
htc
->
ar
);
}
...
...
drivers/net/wireless/ath/ath10k/htc.h
View file @
75ae83d6
...
...
@@ -276,6 +276,7 @@ struct ath10k_htc_ops {
struct
ath10k_htc_ep_ops
{
void
(
*
ep_tx_complete
)(
struct
ath10k
*
,
struct
sk_buff
*
);
void
(
*
ep_rx_complete
)(
struct
ath10k
*
,
struct
sk_buff
*
);
void
(
*
ep_tx_credits
)(
struct
ath10k
*
);
};
/* service connection information */
...
...
@@ -315,15 +316,11 @@ struct ath10k_htc_ep {
int
ul_is_polled
;
/* call HIF to get tx completions */
int
dl_is_polled
;
/* call HIF to fetch rx (not implemented) */
struct
sk_buff_head
tx_queue
;
u8
seq_no
;
/* for debugging */
int
tx_credits
;
int
tx_credit_size
;
int
tx_credits_per_max_message
;
bool
tx_credit_flow_enabled
;
struct
work_struct
send_work
;
};
struct
ath10k_htc_svc_tx_credits
{
...
...
drivers/net/wireless/ath/ath10k/htt.h
View file @
75ae83d6
...
...
@@ -19,6 +19,7 @@
#define _HTT_H_
#include <linux/bug.h>
#include <linux/interrupt.h>
#include "htc.h"
#include "rx_desc.h"
...
...
@@ -1268,6 +1269,7 @@ struct ath10k_htt {
/* set if host-fw communication goes haywire
* used to avoid further failures */
bool
rx_confused
;
struct
tasklet_struct
rx_replenish_task
;
};
#define RX_HTT_HDR_STATUS_LEN 64
...
...
@@ -1308,6 +1310,10 @@ struct htt_rx_desc {
#define HTT_RX_BUF_SIZE 1920
#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle
* aggregated traffic more nicely. */
#define ATH10K_HTT_MAX_NUM_REFILL 16
/*
* DMA_MAP expects the buffer to be an integral number of cache lines.
* Rather than checking the actual cache line size, this code makes a
...
...
@@ -1327,6 +1333,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt);
void
ath10k_htt_htc_tx_complete
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
);
void
ath10k_htt_t2h_msg_handler
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
);
int
ath10k_htt_h2t_ver_req_msg
(
struct
ath10k_htt
*
htt
);
int
ath10k_htt_h2t_stats_req
(
struct
ath10k_htt
*
htt
,
u8
mask
,
u64
cookie
);
int
ath10k_htt_send_rx_ring_cfg_ll
(
struct
ath10k_htt
*
htt
);
void
__ath10k_htt_tx_dec_pending
(
struct
ath10k_htt
*
htt
);
...
...
drivers/net/wireless/ath/ath10k/htt_rx.c
View file @
75ae83d6
...
...
@@ -20,6 +20,7 @@
#include "htt.h"
#include "txrx.h"
#include "debug.h"
#include "trace.h"
#include <linux/log2.h>
...
...
@@ -40,6 +41,10 @@
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
static
int
ath10k_htt_rx_get_csum_state
(
struct
sk_buff
*
skb
);
static
int
ath10k_htt_rx_ring_size
(
struct
ath10k_htt
*
htt
)
{
int
size
;
...
...
@@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
static
void
ath10k_htt_rx_msdu_buff_replenish
(
struct
ath10k_htt
*
htt
)
{
int
ret
,
num_to_fill
;
int
ret
,
num_
deficit
,
num_
to_fill
;
/* Refilling the whole RX ring buffer proves to be a bad idea. The
* reason is RX may take up significant amount of CPU cycles and starve
* other tasks, e.g. TX on an ethernet device while acting as a bridge
* with ath10k wlan interface. This ended up with very poor performance
* once CPU the host system was overwhelmed with RX on ath10k.
*
* By limiting the number of refills the replenishing occurs
* progressively. This in turns makes use of the fact tasklets are
* processed in FIFO order. This means actual RX processing can starve
* out refilling. If there's not enough buffers on RX ring FW will not
* report RX until it is refilled with enough buffers. This
* automatically balances load wrt to CPU power.
*
* This probably comes at a cost of lower maximum throughput but
* improves the avarage and stability. */
spin_lock_bh
(
&
htt
->
rx_ring
.
lock
);
num_to_fill
=
htt
->
rx_ring
.
fill_level
-
htt
->
rx_ring
.
fill_cnt
;
num_deficit
=
htt
->
rx_ring
.
fill_level
-
htt
->
rx_ring
.
fill_cnt
;
num_to_fill
=
min
(
ATH10K_HTT_MAX_NUM_REFILL
,
num_deficit
);
num_deficit
-=
num_to_fill
;
ret
=
ath10k_htt_rx_ring_fill_n
(
htt
,
num_to_fill
);
if
(
ret
==
-
ENOMEM
)
{
/*
...
...
@@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
*/
mod_timer
(
&
htt
->
rx_ring
.
refill_retry_timer
,
jiffies
+
msecs_to_jiffies
(
HTT_RX_RING_REFILL_RETRY_MS
));
}
else
if
(
num_deficit
>
0
)
{
tasklet_schedule
(
&
htt
->
rx_replenish_task
);
}
spin_unlock_bh
(
&
htt
->
rx_ring
.
lock
);
}
...
...
@@ -212,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
int
sw_rd_idx
=
htt
->
rx_ring
.
sw_rd_idx
.
msdu_payld
;
del_timer_sync
(
&
htt
->
rx_ring
.
refill_retry_timer
);
tasklet_kill
(
&
htt
->
rx_replenish_task
);
while
(
sw_rd_idx
!=
__le32_to_cpu
(
*
(
htt
->
rx_ring
.
alloc_idx
.
vaddr
)))
{
struct
sk_buff
*
skb
=
...
...
@@ -441,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
return
msdu_chaining
;
}
static
void
ath10k_htt_rx_replenish_task
(
unsigned
long
ptr
)
{
struct
ath10k_htt
*
htt
=
(
struct
ath10k_htt
*
)
ptr
;
ath10k_htt_rx_msdu_buff_replenish
(
htt
);
}
int
ath10k_htt_rx_attach
(
struct
ath10k_htt
*
htt
)
{
dma_addr_t
paddr
;
...
...
@@ -501,7 +532,10 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
if
(
__ath10k_htt_rx_ring_fill_n
(
htt
,
htt
->
rx_ring
.
fill_level
))
goto
err_fill_ring
;
ath10k_dbg
(
ATH10K_DBG_HTT
,
"HTT RX ring size: %d, fill_level: %d
\n
"
,
tasklet_init
(
&
htt
->
rx_replenish_task
,
ath10k_htt_rx_replenish_task
,
(
unsigned
long
)
htt
);
ath10k_dbg
(
ATH10K_DBG_BOOT
,
"htt rx ring size %d fill_level %d
\n
"
,
htt
->
rx_ring
.
size
,
htt
->
rx_ring
.
fill_level
);
return
0
;
...
...
@@ -590,142 +624,144 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
return
false
;
}
static
int
ath10k_htt_rx_amsdu
(
struct
ath10k_htt
*
htt
,
struct
htt_rx_info
*
info
)
struct
rfc1042_hdr
{
u8
llc_dsap
;
u8
llc_ssap
;
u8
llc_ctrl
;
u8
snap_oui
[
3
];
__be16
snap_type
;
}
__packed
;
struct
amsdu_subframe_hdr
{
u8
dst
[
ETH_ALEN
];
u8
src
[
ETH_ALEN
];
__be16
len
;
}
__packed
;
static
void
ath10k_htt_rx_amsdu
(
struct
ath10k_htt
*
htt
,
struct
htt_rx_info
*
info
)
{
struct
htt_rx_desc
*
rxd
;
struct
sk_buff
*
amsdu
;
struct
sk_buff
*
first
;
struct
ieee80211_hdr
*
hdr
;
struct
sk_buff
*
skb
=
info
->
skb
;
enum
rx_msdu_decap_format
fmt
;
enum
htt_rx_mpdu_encrypt_type
enctype
;
struct
ieee80211_hdr
*
hdr
;
u8
hdr_buf
[
64
],
addr
[
ETH_ALEN
],
*
qos
;
unsigned
int
hdr_len
;
int
crypto_len
;
rxd
=
(
void
*
)
skb
->
data
-
sizeof
(
*
rxd
);
fmt
=
MS
(
__le32_to_cpu
(
rxd
->
msdu_start
.
info1
),
RX_MSDU_START_INFO1_DECAP_FORMAT
);
enctype
=
MS
(
__le32_to_cpu
(
rxd
->
mpdu_start
.
info0
),
RX_MPDU_START_INFO0_ENCRYPT_TYPE
);
/* FIXME: No idea what assumptions are safe here. Need logs */
if
((
fmt
==
RX_MSDU_DECAP_RAW
&&
skb
->
next
))
{
ath10k_htt_rx_free_msdu_chain
(
skb
->
next
);
skb
->
next
=
NULL
;
return
-
ENOTSUPP
;
}
hdr
=
(
struct
ieee80211_hdr
*
)
rxd
->
rx_hdr_status
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
memcpy
(
hdr_buf
,
hdr
,
hdr_len
);
hdr
=
(
struct
ieee80211_hdr
*
)
hdr_buf
;
/*
A-MSDU max is a little less than 8K */
amsdu
=
dev_alloc_skb
(
8
*
1024
);
if
(
!
amsdu
)
{
ath10k_warn
(
"A-MSDU allocation failed
\n
"
);
ath10k_htt_rx_free_msdu_chain
(
skb
->
next
);
skb
->
next
=
NULL
;
return
-
ENOMEM
;
}
if
(
fmt
>=
RX_MSDU_DECAP_NATIVE_WIFI
)
{
int
hdrlen
;
hdr
=
(
void
*
)
rxd
->
rx_hdr_status
;
hdrlen
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
memcpy
(
skb_put
(
amsdu
,
hdrlen
),
hdr
,
hdrlen
);
}
/*
FIXME: Hopefully this is a temporary measure.
*
* Reporting individual A-MSDU subframes means each reported frame
* shares the same sequence number.
*
* mac80211 drops frames it recognizes as duplicates, i.e.
* retransmission flag is set and sequence number matches sequence
* number from a previous frame (as per IEEE 802.11-2012: 9.3.2.10
* "Duplicate detection and recovery")
*
* To avoid frames being dropped clear retransmission flag for all
* received A-MSDUs.
*
* Worst case: actual duplicate frames will be reported but this should
* still be handled gracefully by other OSI/ISO layers. */
hdr
->
frame_control
&=
cpu_to_le16
(
~
IEEE80211_FCTL_RETRY
);
first
=
skb
;
while
(
skb
)
{
void
*
decap_hdr
;
int
decap_len
=
0
;
int
len
;
rxd
=
(
void
*
)
skb
->
data
-
sizeof
(
*
rxd
);
fmt
=
MS
(
__le32_to_cpu
(
rxd
->
msdu_start
.
info1
),
RX_MSDU_START_INFO1_DECAP_FORMAT
);
RX_MSDU_START_INFO1_DECAP_FORMAT
);
decap_hdr
=
(
void
*
)
rxd
->
rx_hdr_status
;
if
(
skb
==
first
)
{
/* We receive linked A-MSDU subframe skbuffs. The
* first one contains the original 802.11 header (and
* possible crypto param) in the RX descriptor. The
* A-MSDU subframe header follows that. Each part is
* aligned to 4 byte boundary. */
hdr
=
(
void
*
)
amsdu
->
data
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
crypto_len
=
ath10k_htt_rx_crypto_param_len
(
enctype
);
decap_hdr
+=
roundup
(
hdr_len
,
4
);
decap_hdr
+=
roundup
(
crypto_len
,
4
);
}
skb
->
ip_summed
=
ath10k_htt_rx_get_csum_state
(
skb
);
/* When fmt == RX_MSDU_DECAP_8023_SNAP_LLC:
*
* SNAP 802.3 consists of:
* [dst:6][src:6][len:2][dsap:1][ssap:1][ctl:1][snap:5]
* [data][fcs:4].
*
* Since this overlaps with A-MSDU header (da, sa, len)
* there's nothing extra to do. */
if
(
fmt
==
RX_MSDU_DECAP_ETHERNET2_DIX
)
{
/* Ethernet2 decap inserts ethernet header in place of
* A-MSDU subframe header. */
skb_pull
(
skb
,
6
+
6
+
2
);
/* A-MSDU subframe header length */
decap_len
+=
6
+
6
+
2
;
/* Ethernet2 decap also strips the LLC/SNAP so we need
* to re-insert it. The LLC/SNAP follows A-MSDU
* subframe header. */
/* FIXME: Not all LLCs are 8 bytes long */
decap_len
+=
8
;
memcpy
(
skb_put
(
amsdu
,
decap_len
),
decap_hdr
,
decap_len
);
/* First frame in an A-MSDU chain has more decapped data. */
if
(
skb
==
first
)
{
len
=
round_up
(
ieee80211_hdrlen
(
hdr
->
frame_control
),
4
);
len
+=
round_up
(
ath10k_htt_rx_crypto_param_len
(
enctype
),
4
);
decap_hdr
+=
len
;
}
if
(
fmt
==
RX_MSDU_DECAP_NATIVE_WIFI
)
{
/* Native Wifi decap inserts regular 802.11 header
* in place of A-MSDU subframe header. */
switch
(
fmt
)
{
case
RX_MSDU_DECAP_RAW
:
/* remove trailing FCS */
skb_trim
(
skb
,
skb
->
len
-
FCS_LEN
);
break
;
case
RX_MSDU_DECAP_NATIVE_WIFI
:
/* pull decapped header and copy DA */
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
skb_pull
(
skb
,
ieee80211_hdrlen
(
hdr
->
frame_control
));
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
memcpy
(
addr
,
ieee80211_get_DA
(
hdr
),
ETH_ALEN
);
skb_pull
(
skb
,
hdr_len
);
/* A-MSDU subframe header length */
decap_len
+=
6
+
6
+
2
;
/* push original 802.11 header */
hdr
=
(
struct
ieee80211_hdr
*
)
hdr_buf
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
memcpy
(
skb_put
(
amsdu
,
decap_len
),
decap_hdr
,
decap_len
);
}
/* original A-MSDU header has the bit set but we're
* not including A-MSDU subframe header */
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
qos
=
ieee80211_get_qos_ctl
(
hdr
);
qos
[
0
]
&=
~
IEEE80211_QOS_CTL_A_MSDU_PRESENT
;
if
(
fmt
==
RX_MSDU_DECAP_RAW
)
skb_trim
(
skb
,
skb
->
len
-
4
);
/* remove FCS */
/* original 802.11 header has a different DA */
memcpy
(
ieee80211_get_DA
(
hdr
),
addr
,
ETH_ALEN
);
break
;
case
RX_MSDU_DECAP_ETHERNET2_DIX
:
/* strip ethernet header and insert decapped 802.11
* header, amsdu subframe header and rfc1042 header */
memcpy
(
skb_put
(
amsdu
,
skb
->
len
),
skb
->
data
,
skb
->
len
);
len
=
0
;
len
+=
sizeof
(
struct
rfc1042_hdr
);
len
+=
sizeof
(
struct
amsdu_subframe_hdr
);
/* A-MSDU subframes are padded to 4bytes
* but relative to first subframe, not the whole MPDU */
if
(
skb
->
next
&&
((
decap_len
+
skb
->
len
)
&
3
))
{
int
padlen
=
4
-
((
decap_len
+
skb
->
len
)
&
3
);
memset
(
skb_put
(
amsdu
,
padlen
),
0
,
padlen
);
skb_pull
(
skb
,
sizeof
(
struct
ethhdr
));
memcpy
(
skb_push
(
skb
,
len
),
decap_hdr
,
len
);
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
break
;
case
RX_MSDU_DECAP_8023_SNAP_LLC
:
/* insert decapped 802.11 header making a singly
* A-MSDU */
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
break
;
}
info
->
skb
=
skb
;
info
->
encrypt_type
=
enctype
;
skb
=
skb
->
next
;
}
info
->
skb
=
amsdu
;
info
->
encrypt_type
=
enctype
;
info
->
skb
->
next
=
NULL
;
ath10k_htt_rx_free_msdu_chain
(
first
);
ath10k_process_rx
(
htt
->
ar
,
info
);
}
return
0
;
/* FIXME: It might be nice to re-assemble the A-MSDU when there's a
* monitor interface active for sniffing purposes. */
}
static
int
ath10k_htt_rx_msdu
(
struct
ath10k_htt
*
htt
,
struct
htt_rx_info
*
info
)
static
void
ath10k_htt_rx_msdu
(
struct
ath10k_htt
*
htt
,
struct
htt_rx_info
*
info
)
{
struct
sk_buff
*
skb
=
info
->
skb
;
struct
htt_rx_desc
*
rxd
;
struct
ieee80211_hdr
*
hdr
;
enum
rx_msdu_decap_format
fmt
;
enum
htt_rx_mpdu_encrypt_type
enctype
;
int
hdr_len
;
void
*
rfc1042
;
/* This shouldn't happen. If it does than it may be a FW bug. */
if
(
skb
->
next
)
{
...
...
@@ -739,49 +775,53 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
RX_MSDU_START_INFO1_DECAP_FORMAT
);
enctype
=
MS
(
__le32_to_cpu
(
rxd
->
mpdu_start
.
info0
),
RX_MPDU_START_INFO0_ENCRYPT_TYPE
);
hdr
=
(
void
*
)
skb
->
data
-
RX_HTT_HDR_STATUS_LEN
;
hdr
=
(
struct
ieee80211_hdr
*
)
rxd
->
rx_hdr_status
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
skb
->
ip_summed
=
ath10k_htt_rx_get_csum_state
(
skb
);
switch
(
fmt
)
{
case
RX_MSDU_DECAP_RAW
:
/* remove trailing FCS */
skb_trim
(
skb
,
skb
->
len
-
4
);
skb_trim
(
skb
,
skb
->
len
-
FCS_LEN
);
break
;
case
RX_MSDU_DECAP_NATIVE_WIFI
:
/* nothing to do here */
/* Pull decapped header */
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
skb_pull
(
skb
,
hdr_len
);
/* Push original header */
hdr
=
(
struct
ieee80211_hdr
*
)
rxd
->
rx_hdr_status
;
hdr_len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
break
;
case
RX_MSDU_DECAP_ETHERNET2_DIX
:
/* macaddr[6] + macaddr[6] + ethertype[2] */
skb_pull
(
skb
,
6
+
6
+
2
);
break
;
case
RX_MSDU_DECAP_8023_SNAP_LLC
:
/* macaddr[6] + macaddr[6] + len[2] */
/* we don't need this for non-A-MSDU */
skb_pull
(
skb
,
6
+
6
+
2
);
break
;
}
if
(
fmt
==
RX_MSDU_DECAP_ETHERNET2_DIX
)
{
void
*
llc
;
int
llclen
;
/* strip ethernet header and insert decapped 802.11 header and
* rfc1042 header */
llclen
=
8
;
llc
=
hdr
;
llc
+=
roundup
(
ieee80211_hdrlen
(
hdr
->
frame_control
),
4
);
llc
+=
roundup
(
ath10k_htt_rx_crypto_param_len
(
enctype
),
4
);
rfc1042
=
hdr
;
rfc1042
+=
roundup
(
hdr_len
,
4
);
rfc1042
+=
roundup
(
ath10k_htt_rx_crypto_param_len
(
enctype
),
4
);
skb_push
(
skb
,
llclen
);
memcpy
(
skb
->
data
,
llc
,
llclen
);
}
skb_pull
(
skb
,
sizeof
(
struct
ethhdr
));
memcpy
(
skb_push
(
skb
,
sizeof
(
struct
rfc1042_hdr
)),
rfc1042
,
sizeof
(
struct
rfc1042_hdr
));
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
break
;
case
RX_MSDU_DECAP_8023_SNAP_LLC
:
/* remove A-MSDU subframe header and insert
* decapped 802.11 header. rfc1042 header is already there */
if
(
fmt
>=
RX_MSDU_DECAP_ETHERNET2_DIX
)
{
int
len
=
ieee80211_hdrlen
(
hdr
->
frame_control
);
skb_push
(
skb
,
len
);
memcpy
(
skb
->
data
,
hdr
,
len
);
skb_pull
(
skb
,
sizeof
(
struct
amsdu_subframe_hdr
));
memcpy
(
skb_push
(
skb
,
hdr_len
),
hdr
,
hdr_len
);
break
;
}
info
->
skb
=
skb
;
info
->
encrypt_type
=
enctype
;
return
0
;
ath10k_process_rx
(
htt
->
ar
,
info
);
}
static
bool
ath10k_htt_rx_has_decrypt_err
(
struct
sk_buff
*
skb
)
...
...
@@ -853,8 +893,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
int
fw_desc_len
;
u8
*
fw_desc
;
int
i
,
j
;
int
ret
;
int
ip_summed
;
memset
(
&
info
,
0
,
sizeof
(
info
));
...
...
@@ -929,11 +967,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
continue
;
}
/* The skb is not yet processed and it may be
* reallocated. Since the offload is in the original
* skb extract the checksum now and assign it later */
ip_summed
=
ath10k_htt_rx_get_csum_state
(
msdu_head
);
info
.
skb
=
msdu_head
;
info
.
fcs_err
=
ath10k_htt_rx_has_fcs_err
(
msdu_head
);
info
.
signal
=
ATH10K_DEFAULT_NOISE_FLOOR
;
...
...
@@ -946,28 +979,13 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
hdr
=
ath10k_htt_rx_skb_get_hdr
(
msdu_head
);
if
(
ath10k_htt_rx_hdr_is_amsdu
(
hdr
))
ret
=
ath10k_htt_rx_amsdu
(
htt
,
&
info
);
ath10k_htt_rx_amsdu
(
htt
,
&
info
);
else
ret
=
ath10k_htt_rx_msdu
(
htt
,
&
info
);
if
(
ret
&&
!
info
.
fcs_err
)
{
ath10k_warn
(
"error processing msdus %d
\n
"
,
ret
);
dev_kfree_skb_any
(
info
.
skb
);
continue
;
}
if
(
ath10k_htt_rx_hdr_is_amsdu
((
void
*
)
info
.
skb
->
data
))
ath10k_dbg
(
ATH10K_DBG_HTT
,
"htt mpdu is amsdu
\n
"
);
info
.
skb
->
ip_summed
=
ip_summed
;
ath10k_dbg_dump
(
ATH10K_DBG_HTT_DUMP
,
NULL
,
"htt mpdu: "
,
info
.
skb
->
data
,
info
.
skb
->
len
);
ath10k_process_rx
(
htt
->
ar
,
&
info
);
ath10k_htt_rx_msdu
(
htt
,
&
info
);
}
}
ath10k_htt_rx_msdu_buff_replenish
(
htt
);
tasklet_schedule
(
&
htt
->
rx_replenish_task
);
}
static
void
ath10k_htt_rx_frag_handler
(
struct
ath10k_htt
*
htt
,
...
...
@@ -1139,7 +1157,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break
;
}
ath10k_txrx_tx_
completed
(
htt
,
&
tx_done
);
ath10k_txrx_tx_
unref
(
htt
,
&
tx_done
);
break
;
}
case
HTT_T2H_MSG_TYPE_TX_COMPL_IND
:
{
...
...
@@ -1173,7 +1191,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
for
(
i
=
0
;
i
<
resp
->
data_tx_completion
.
num_msdus
;
i
++
)
{
msdu_id
=
resp
->
data_tx_completion
.
msdus
[
i
];
tx_done
.
msdu_id
=
__le16_to_cpu
(
msdu_id
);
ath10k_txrx_tx_
completed
(
htt
,
&
tx_done
);
ath10k_txrx_tx_
unref
(
htt
,
&
tx_done
);
}
break
;
}
...
...
@@ -1198,8 +1216,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case
HTT_T2H_MSG_TYPE_TEST
:
/* FIX THIS */
break
;
case
HTT_T2H_MSG_TYPE_TX_INSPECT_IND
:
case
HTT_T2H_MSG_TYPE_STATS_CONF
:
trace_ath10k_htt_stats
(
skb
->
data
,
skb
->
len
);
break
;
case
HTT_T2H_MSG_TYPE_TX_INSPECT_IND
:
case
HTT_T2H_MSG_TYPE_RX_ADDBA
:
case
HTT_T2H_MSG_TYPE_RX_DELBA
:
case
HTT_T2H_MSG_TYPE_RX_FLUSH
:
...
...
drivers/net/wireless/ath/ath10k/htt_tx.c
View file @
75ae83d6
...
...
@@ -96,7 +96,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
htt
->
max_num_pending_tx
=
ath10k_hif_get_free_queue_number
(
htt
->
ar
,
pipe
);
ath10k_dbg
(
ATH10K_DBG_
HT
T
,
"htt tx max num pending tx %d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_
BOO
T
,
"htt tx max num pending tx %d
\n
"
,
htt
->
max_num_pending_tx
);
htt
->
pending_tx
=
kzalloc
(
sizeof
(
*
htt
->
pending_tx
)
*
...
...
@@ -117,7 +117,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
static
void
ath10k_htt_tx_cleanup_pending
(
struct
ath10k_htt
*
htt
)
{
struct
sk_buff
*
txdesc
;
struct
htt_tx_done
tx_done
=
{
0
}
;
int
msdu_id
;
/* No locks needed. Called after communication with the device has
...
...
@@ -127,18 +127,13 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
if
(
!
test_bit
(
msdu_id
,
htt
->
used_msdu_ids
))
continue
;
txdesc
=
htt
->
pending_tx
[
msdu_id
];
if
(
!
txdesc
)
continue
;
ath10k_dbg
(
ATH10K_DBG_HTT
,
"force cleanup msdu_id %hu
\n
"
,
msdu_id
);
if
(
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
refcount
>
0
)
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
refcount
=
1
;
tx_done
.
discard
=
1
;
tx_done
.
msdu_id
=
msdu_id
;
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
discard
=
true
;
ath10k_txrx_tx_unref
(
htt
,
txdesc
);
ath10k_txrx_tx_unref
(
htt
,
&
tx_done
);
}
}
...
...
@@ -152,26 +147,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
void
ath10k_htt_htc_tx_complete
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
{
struct
ath10k_skb_cb
*
skb_cb
=
ATH10K_SKB_CB
(
skb
);
struct
ath10k_htt
*
htt
=
&
ar
->
htt
;
if
(
skb_cb
->
htt
.
is_conf
)
{
dev_kfree_skb_any
(
skb
);
return
;
}
if
(
skb_cb
->
is_aborted
)
{
skb_cb
->
htt
.
discard
=
true
;
/* if the skbuff is aborted we need to make sure we'll free up
* the tx resources, we can't simply run tx_unref() 2 times
* because if htt tx completion came in earlier we'd access
* unallocated memory */
if
(
skb_cb
->
htt
.
refcount
>
1
)
skb_cb
->
htt
.
refcount
=
1
;
}
ath10k_txrx_tx_unref
(
htt
,
skb
);
dev_kfree_skb_any
(
skb
);
}
int
ath10k_htt_h2t_ver_req_msg
(
struct
ath10k_htt
*
htt
)
...
...
@@ -192,10 +168,48 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
cmd
=
(
struct
htt_cmd
*
)
skb
->
data
;
cmd
->
hdr
.
msg_type
=
HTT_H2T_MSG_TYPE_VERSION_REQ
;
ATH10K_SKB_CB
(
skb
)
->
htt
.
is_conf
=
true
;
ret
=
ath10k_htc_send
(
&
htt
->
ar
->
htc
,
htt
->
eid
,
skb
);
if
(
ret
)
{
dev_kfree_skb_any
(
skb
);
return
ret
;
}
return
0
;
}
int
ath10k_htt_h2t_stats_req
(
struct
ath10k_htt
*
htt
,
u8
mask
,
u64
cookie
)
{
struct
htt_stats_req
*
req
;
struct
sk_buff
*
skb
;
struct
htt_cmd
*
cmd
;
int
len
=
0
,
ret
;
len
+=
sizeof
(
cmd
->
hdr
);
len
+=
sizeof
(
cmd
->
stats_req
);
skb
=
ath10k_htc_alloc_skb
(
len
);
if
(
!
skb
)
return
-
ENOMEM
;
skb_put
(
skb
,
len
);
cmd
=
(
struct
htt_cmd
*
)
skb
->
data
;
cmd
->
hdr
.
msg_type
=
HTT_H2T_MSG_TYPE_STATS_REQ
;
req
=
&
cmd
->
stats_req
;
memset
(
req
,
0
,
sizeof
(
*
req
));
/* currently we support only max 8 bit masks so no need to worry
* about endian support */
req
->
upload_types
[
0
]
=
mask
;
req
->
reset_types
[
0
]
=
mask
;
req
->
stat_type
=
HTT_STATS_REQ_CFG_STAT_TYPE_INVALID
;
req
->
cookie_lsb
=
cpu_to_le32
(
cookie
&
0xffffffff
);
req
->
cookie_msb
=
cpu_to_le32
((
cookie
&
0xffffffff00000000ULL
)
>>
32
);
ret
=
ath10k_htc_send
(
&
htt
->
ar
->
htc
,
htt
->
eid
,
skb
);
if
(
ret
)
{
ath10k_warn
(
"failed to send htt type stats request: %d"
,
ret
);
dev_kfree_skb_any
(
skb
);
return
ret
;
}
...
...
@@ -279,8 +293,6 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
#undef desc_offset
ATH10K_SKB_CB
(
skb
)
->
htt
.
is_conf
=
true
;
ret
=
ath10k_htc_send
(
&
htt
->
ar
->
htc
,
htt
->
eid
,
skb
);
if
(
ret
)
{
dev_kfree_skb_any
(
skb
);
...
...
@@ -293,10 +305,10 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
int
ath10k_htt_mgmt_tx
(
struct
ath10k_htt
*
htt
,
struct
sk_buff
*
msdu
)
{
struct
device
*
dev
=
htt
->
ar
->
dev
;
struct
ath10k_skb_cb
*
skb_cb
;
struct
sk_buff
*
txdesc
=
NULL
;
struct
htt_cmd
*
cmd
;
u8
vdev_id
=
ATH10K_SKB_CB
(
msdu
)
->
htt
.
vdev_id
;
struct
ath10k_skb_cb
*
skb_cb
=
ATH10K_SKB_CB
(
msdu
);
u8
vdev_id
=
skb_cb
->
htt
.
vdev_id
;
int
len
=
0
;
int
msdu_id
=
-
1
;
int
res
;
...
...
@@ -304,30 +316,30 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res
=
ath10k_htt_tx_inc_pending
(
htt
);
if
(
res
)
return
res
;
goto
err
;
len
+=
sizeof
(
cmd
->
hdr
);
len
+=
sizeof
(
cmd
->
mgmt_tx
);
txdesc
=
ath10k_htc_alloc_skb
(
len
);
if
(
!
txdesc
)
{
res
=
-
ENOMEM
;
goto
err
;
}
spin_lock_bh
(
&
htt
->
tx_lock
);
msdu_id
=
ath10k_htt_tx_alloc_msdu_id
(
htt
);
if
(
msdu_id
<
0
)
{
res
=
ath10k_htt_tx_alloc_msdu_id
(
htt
);
if
(
res
<
0
)
{
spin_unlock_bh
(
&
htt
->
tx_lock
);
res
=
msdu_id
;
goto
err
;
goto
err_tx_dec
;
}
htt
->
pending_tx
[
msdu_id
]
=
txdesc
;
msdu_id
=
res
;
htt
->
pending_tx
[
msdu_id
]
=
msdu
;
spin_unlock_bh
(
&
htt
->
tx_lock
);
txdesc
=
ath10k_htc_alloc_skb
(
len
);
if
(
!
txdesc
)
{
res
=
-
ENOMEM
;
goto
err_free_msdu_id
;
}
res
=
ath10k_skb_map
(
dev
,
msdu
);
if
(
res
)
goto
err
;
goto
err
_free_txdesc
;
skb_put
(
txdesc
,
len
);
cmd
=
(
struct
htt_cmd
*
)
txdesc
->
data
;
...
...
@@ -339,31 +351,27 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
memcpy
(
cmd
->
mgmt_tx
.
hdr
,
msdu
->
data
,
min_t
(
int
,
msdu
->
len
,
HTT_MGMT_FRM_HDR_DOWNLOAD_LEN
));
/* refcount is decremented by HTC and HTT completions until it reaches
* zero and is freed */
skb_cb
=
ATH10K_SKB_CB
(
txdesc
);
skb_cb
->
htt
.
msdu_id
=
msdu_id
;
skb_cb
->
htt
.
refcount
=
2
;
skb_cb
->
htt
.
msdu
=
msdu
;
skb_cb
->
htt
.
frag_len
=
0
;
skb_cb
->
htt
.
pad_len
=
0
;
res
=
ath10k_htc_send
(
&
htt
->
ar
->
htc
,
htt
->
eid
,
txdesc
);
if
(
res
)
goto
err
;
goto
err
_unmap_msdu
;
return
0
;
err:
err
_unmap_msdu
:
ath10k_skb_unmap
(
dev
,
msdu
);
if
(
txdesc
)
dev_kfree_skb_any
(
txdesc
);
if
(
msdu_id
>=
0
)
{
spin_lock_bh
(
&
htt
->
tx_lock
);
htt
->
pending_tx
[
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
msdu_id
);
spin_unlock_bh
(
&
htt
->
tx_lock
);
}
err_free_txdesc:
dev_kfree_skb_any
(
txdesc
);
err_free_msdu_id:
spin_lock_bh
(
&
htt
->
tx_lock
);
htt
->
pending_tx
[
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
msdu_id
);
spin_unlock_bh
(
&
htt
->
tx_lock
);
err_tx_dec:
ath10k_htt_tx_dec_pending
(
htt
);
err:
return
res
;
}
...
...
@@ -373,13 +381,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
struct
htt_cmd
*
cmd
;
struct
htt_data_tx_desc_frag
*
tx_frags
;
struct
ieee80211_hdr
*
hdr
=
(
struct
ieee80211_hdr
*
)
msdu
->
data
;
struct
ath10k_skb_cb
*
skb_cb
;
struct
ath10k_skb_cb
*
skb_cb
=
ATH10K_SKB_CB
(
msdu
)
;
struct
sk_buff
*
txdesc
=
NULL
;
struct
sk_buff
*
txfrag
=
NULL
;
bool
use_frags
;
u8
vdev_id
=
ATH10K_SKB_CB
(
msdu
)
->
htt
.
vdev_id
;
u8
tid
;
int
prefetch_len
,
desc_len
,
frag_len
;
dma_addr_t
frags_paddr
;
int
prefetch_len
,
desc_len
;
int
msdu_id
=
-
1
;
int
res
;
u8
flags0
;
...
...
@@ -387,73 +394,73 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res
=
ath10k_htt_tx_inc_pending
(
htt
);
if
(
res
)
return
res
;
goto
err
;
spin_lock_bh
(
&
htt
->
tx_lock
);
res
=
ath10k_htt_tx_alloc_msdu_id
(
htt
);
if
(
res
<
0
)
{
spin_unlock_bh
(
&
htt
->
tx_lock
);
goto
err_tx_dec
;
}
msdu_id
=
res
;
htt
->
pending_tx
[
msdu_id
]
=
msdu
;
spin_unlock_bh
(
&
htt
->
tx_lock
);
prefetch_len
=
min
(
htt
->
prefetch_len
,
msdu
->
len
);
prefetch_len
=
roundup
(
prefetch_len
,
4
);
desc_len
=
sizeof
(
cmd
->
hdr
)
+
sizeof
(
cmd
->
data_tx
)
+
prefetch_len
;
frag_len
=
sizeof
(
*
tx_frags
)
*
2
;
txdesc
=
ath10k_htc_alloc_skb
(
desc_len
);
if
(
!
txdesc
)
{
res
=
-
ENOMEM
;
goto
err
;
goto
err
_free_msdu_id
;
}
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if
(
htt
->
target_version_major
<
3
||
!
ieee80211_is_mgmt
(
hdr
->
frame_control
))
{
txfrag
=
dev_alloc_skb
(
frag_len
);
if
(
!
txfrag
)
{
res
=
-
ENOMEM
;
goto
err
;
}
}
use_frags
=
htt
->
target_version_major
<
3
||
!
ieee80211_is_mgmt
(
hdr
->
frame_control
);
if
(
!
IS_ALIGNED
((
unsigned
long
)
txdesc
->
data
,
4
))
{
ath10k_warn
(
"htt alignment check failed. dropping packet.
\n
"
);
res
=
-
EIO
;
goto
err
;
goto
err
_free_txdesc
;
}
spin_lock_bh
(
&
htt
->
tx_lock
);
msdu_id
=
ath10k_htt_tx_alloc_msdu_id
(
htt
);
if
(
msdu_id
<
0
)
{
spin_unlock_bh
(
&
htt
->
tx_lock
);
res
=
msdu_id
;
goto
err
;
if
(
use_frags
)
{
skb_cb
->
htt
.
frag_len
=
sizeof
(
*
tx_frags
)
*
2
;
skb_cb
->
htt
.
pad_len
=
(
unsigned
long
)
msdu
->
data
-
round_down
((
unsigned
long
)
msdu
->
data
,
4
);
skb_push
(
msdu
,
skb_cb
->
htt
.
frag_len
+
skb_cb
->
htt
.
pad_len
);
}
else
{
skb_cb
->
htt
.
frag_len
=
0
;
skb_cb
->
htt
.
pad_len
=
0
;
}
htt
->
pending_tx
[
msdu_id
]
=
txdesc
;
spin_unlock_bh
(
&
htt
->
tx_lock
);
res
=
ath10k_skb_map
(
dev
,
msdu
);
if
(
res
)
goto
err
;
goto
err_pull_txfrag
;
if
(
use_frags
)
{
dma_sync_single_for_cpu
(
dev
,
skb_cb
->
paddr
,
msdu
->
len
,
DMA_TO_DEVICE
);
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if
(
htt
->
target_version_major
<
3
||
!
ieee80211_is_mgmt
(
hdr
->
frame_control
))
{
/* tx fragment list must be terminated with zero-entry */
skb_put
(
txfrag
,
frag_len
);
tx_frags
=
(
struct
htt_data_tx_desc_frag
*
)
txfrag
->
data
;
tx_frags
[
0
].
paddr
=
__cpu_to_le32
(
ATH10K_SKB_CB
(
msdu
)
->
paddr
);
tx_frags
[
0
].
len
=
__cpu_to_le32
(
msdu
->
len
);
tx_frags
=
(
struct
htt_data_tx_desc_frag
*
)
msdu
->
data
;
tx_frags
[
0
].
paddr
=
__cpu_to_le32
(
skb_cb
->
paddr
+
skb_cb
->
htt
.
frag_len
+
skb_cb
->
htt
.
pad_len
);
tx_frags
[
0
].
len
=
__cpu_to_le32
(
msdu
->
len
-
skb_cb
->
htt
.
frag_len
-
skb_cb
->
htt
.
pad_len
);
tx_frags
[
1
].
paddr
=
__cpu_to_le32
(
0
);
tx_frags
[
1
].
len
=
__cpu_to_le32
(
0
);
res
=
ath10k_skb_map
(
dev
,
txfrag
);
if
(
res
)
goto
err
;
ath10k_dbg
(
ATH10K_DBG_HTT
,
"txfrag 0x%llx
\n
"
,
(
unsigned
long
long
)
ATH10K_SKB_CB
(
txfrag
)
->
paddr
);
ath10k_dbg_dump
(
ATH10K_DBG_HTT_DUMP
,
NULL
,
"txfrag: "
,
txfrag
->
data
,
frag_len
);
dma_sync_single_for_device
(
dev
,
skb_cb
->
paddr
,
msdu
->
len
,
DMA_TO_DEVICE
);
}
ath10k_dbg
(
ATH10K_DBG_HTT
,
"msdu 0x%llx
\n
"
,
...
...
@@ -463,7 +470,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
skb_put
(
txdesc
,
desc_len
);
cmd
=
(
struct
htt_cmd
*
)
txdesc
->
data
;
memset
(
cmd
,
0
,
desc_len
);
tid
=
ATH10K_SKB_CB
(
msdu
)
->
htt
.
tid
;
...
...
@@ -474,15 +480,11 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
flags0
|=
HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT
;
flags0
|=
HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT
;
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if
(
htt
->
target_version_major
>=
3
&&
ieee80211_is_mgmt
(
hdr
->
frame_control
))
flags0
|=
SM
(
ATH10K_HW_TXRX_MGMT
,
if
(
use_frags
)
flags0
|=
SM
(
ATH10K_HW_TXRX_NATIVE_WIFI
,
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE
);
else
flags0
|=
SM
(
ATH10K_HW_TXRX_
NATIVE_WIFI
,
flags0
|=
SM
(
ATH10K_HW_TXRX_
MGMT
,
HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE
);
flags1
=
0
;
...
...
@@ -491,52 +493,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
flags1
|=
HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD
;
flags1
|=
HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD
;
/* Since HTT 3.0 there is no separate mgmt tx command. However in case
* of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx
* fragment list host driver specifies directly frame pointer. */
if
(
htt
->
target_version_major
>=
3
&&
ieee80211_is_mgmt
(
hdr
->
frame_control
))
frags_paddr
=
ATH10K_SKB_CB
(
msdu
)
->
paddr
;
else
frags_paddr
=
ATH10K_SKB_CB
(
txfrag
)
->
paddr
;
cmd
->
hdr
.
msg_type
=
HTT_H2T_MSG_TYPE_TX_FRM
;
cmd
->
data_tx
.
flags0
=
flags0
;
cmd
->
data_tx
.
flags1
=
__cpu_to_le16
(
flags1
);
cmd
->
data_tx
.
len
=
__cpu_to_le16
(
msdu
->
len
);
cmd
->
data_tx
.
len
=
__cpu_to_le16
(
msdu
->
len
-
skb_cb
->
htt
.
frag_len
-
skb_cb
->
htt
.
pad_len
);
cmd
->
data_tx
.
id
=
__cpu_to_le16
(
msdu_id
);
cmd
->
data_tx
.
frags_paddr
=
__cpu_to_le32
(
frags_
paddr
);
cmd
->
data_tx
.
frags_paddr
=
__cpu_to_le32
(
skb_cb
->
paddr
);
cmd
->
data_tx
.
peerid
=
__cpu_to_le32
(
HTT_INVALID_PEERID
);
memcpy
(
cmd
->
data_tx
.
prefetch
,
msdu
->
data
,
prefetch_len
);
/* refcount is decremented by HTC and HTT completions until it reaches
* zero and is freed */
skb_cb
=
ATH10K_SKB_CB
(
txdesc
);
skb_cb
->
htt
.
msdu_id
=
msdu_id
;
skb_cb
->
htt
.
refcount
=
2
;
skb_cb
->
htt
.
txfrag
=
txfrag
;
skb_cb
->
htt
.
msdu
=
msdu
;
memcpy
(
cmd
->
data_tx
.
prefetch
,
hdr
,
prefetch_len
);
res
=
ath10k_htc_send
(
&
htt
->
ar
->
htc
,
htt
->
eid
,
txdesc
);
if
(
res
)
goto
err
;
goto
err
_unmap_msdu
;
return
0
;
err:
if
(
txfrag
)
ath10k_skb_unmap
(
dev
,
txfrag
);
if
(
txdesc
)
dev_kfree_skb_any
(
txdesc
);
if
(
txfrag
)
dev_kfree_skb_any
(
txfrag
);
if
(
msdu_id
>=
0
)
{
spin_lock_bh
(
&
htt
->
tx_lock
);
htt
->
pending_tx
[
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
msdu_id
);
spin_unlock_bh
(
&
htt
->
tx_lock
);
}
ath10k_htt_tx_dec_pending
(
htt
);
err_unmap_msdu:
ath10k_skb_unmap
(
dev
,
msdu
);
err_pull_txfrag:
skb_pull
(
msdu
,
skb_cb
->
htt
.
frag_len
+
skb_cb
->
htt
.
pad_len
);
err_free_txdesc:
dev_kfree_skb_any
(
txdesc
);
err_free_msdu_id:
spin_lock_bh
(
&
htt
->
tx_lock
);
htt
->
pending_tx
[
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
msdu_id
);
spin_unlock_bh
(
&
htt
->
tx_lock
);
err_tx_dec:
ath10k_htt_tx_dec_pending
(
htt
);
err:
return
res
;
}
drivers/net/wireless/ath/ath10k/hw.h
View file @
75ae83d6
...
...
@@ -74,7 +74,11 @@ enum ath10k_mcast2ucast_mode {
#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_TIMEOUT_LO_PRI 100
#define TARGET_RX_TIMEOUT_HI_PRI 40
#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET
/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and
* avoid a very expensive re-alignment in mac80211. */
#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI
#define TARGET_SCAN_MAX_PENDING_REQS 4
#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3
#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3
...
...
drivers/net/wireless/ath/ath10k/mac.c
View file @
75ae83d6
...
...
@@ -460,6 +460,11 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
arg
.
ssid_len
=
arvif
->
vif
->
bss_conf
.
ssid_len
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d start center_freq %d phymode %s
\n
"
,
arg
.
vdev_id
,
arg
.
channel
.
freq
,
ath10k_wmi_phymode_str
(
arg
.
channel
.
mode
));
ret
=
ath10k_wmi_vdev_start
(
ar
,
&
arg
);
if
(
ret
)
{
ath10k_warn
(
"WMI vdev start failed: ret %d
\n
"
,
ret
);
...
...
@@ -604,7 +609,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
goto
vdev_fail
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
Monitor interface created, vdev id: %
d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac monitor vdev %d create
d
\n
"
,
ar
->
monitor_vdev_id
);
ar
->
monitor_present
=
true
;
...
...
@@ -636,7 +641,7 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
ar
->
free_vdev_map
|=
1
<<
(
ar
->
monitor_vdev_id
);
ar
->
monitor_present
=
false
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
Monitor interface destroyed, vdev id: %
d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac monitor vdev %d delete
d
\n
"
,
ar
->
monitor_vdev_id
);
return
ret
;
}
...
...
@@ -665,7 +670,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
arvif
->
vdev_id
);
return
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
VDEV:
%d up
\n
"
,
arvif
->
vdev_id
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac vdev
%d up
\n
"
,
arvif
->
vdev_id
);
}
static
void
ath10k_control_ibss
(
struct
ath10k_vif
*
arvif
,
...
...
@@ -749,14 +754,14 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
psmode
=
WMI_STA_PS_MODE_DISABLED
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d psmode %s
\n
"
,
arvif
->
vdev_id
,
psmode
?
"enable"
:
"disable"
);
ar_iter
->
ret
=
ath10k_wmi_set_psmode
(
ar_iter
->
ar
,
arvif
->
vdev_id
,
psmode
);
if
(
ar_iter
->
ret
)
ath10k_warn
(
"Failed to set PS Mode: %d for VDEV: %d
\n
"
,
psmode
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set PS Mode: %d for VDEV: %d
\n
"
,
psmode
,
arvif
->
vdev_id
);
}
/**********************/
...
...
@@ -946,7 +951,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg
->
peer_ht_rates
.
num_rates
=
n
;
arg
->
peer_num_spatial_streams
=
max
((
n
+
7
)
/
8
,
1
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mcs cnt %d nss %d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac ht peer %pM mcs cnt %d nss %d
\n
"
,
arg
->
addr
,
arg
->
peer_ht_rates
.
num_rates
,
arg
->
peer_num_spatial_streams
);
}
...
...
@@ -966,7 +972,7 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
arg
->
peer_flags
|=
WMI_PEER_QOS
;
if
(
sta
->
wme
&&
sta
->
uapsd_queues
)
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
uapsd_queues: 0x%X, max_sp:
%d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac uapsd_queues 0x%x max_sp
%d
\n
"
,
sta
->
uapsd_queues
,
sta
->
max_sp
);
arg
->
peer_flags
|=
WMI_PEER_APSD
;
...
...
@@ -1045,7 +1051,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
arg
->
peer_vht_rates
.
tx_mcs_set
=
__le16_to_cpu
(
vht_cap
->
vht_mcs
.
tx_mcs_map
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vht peer
\n
"
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vht peer %pM max_mpdu %d flags 0x%x
\n
"
,
sta
->
addr
,
arg
->
peer_max_mpdu
,
arg
->
peer_flags
);
}
static
void
ath10k_peer_assoc_h_qos
(
struct
ath10k
*
ar
,
...
...
@@ -1073,8 +1080,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
{
enum
wmi_phy_mode
phymode
=
MODE_UNKNOWN
;
/* FIXME: add VHT */
switch
(
ar
->
hw
->
conf
.
chandef
.
chan
->
band
)
{
case
IEEE80211_BAND_2GHZ
:
if
(
sta
->
ht_cap
.
ht_supported
)
{
...
...
@@ -1088,7 +1093,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
break
;
case
IEEE80211_BAND_5GHZ
:
if
(
sta
->
ht_cap
.
ht_supported
)
{
/*
* Check VHT first.
*/
if
(
sta
->
vht_cap
.
vht_supported
)
{
if
(
sta
->
bandwidth
==
IEEE80211_STA_RX_BW_80
)
phymode
=
MODE_11AC_VHT80
;
else
if
(
sta
->
bandwidth
==
IEEE80211_STA_RX_BW_40
)
phymode
=
MODE_11AC_VHT40
;
else
if
(
sta
->
bandwidth
==
IEEE80211_STA_RX_BW_20
)
phymode
=
MODE_11AC_VHT20
;
}
else
if
(
sta
->
ht_cap
.
ht_supported
)
{
if
(
sta
->
bandwidth
==
IEEE80211_STA_RX_BW_40
)
phymode
=
MODE_11NA_HT40
;
else
...
...
@@ -1102,6 +1117,9 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
break
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac peer %pM phymode %s
\n
"
,
sta
->
addr
,
ath10k_wmi_phymode_str
(
phymode
));
arg
->
peer_phymode
=
phymode
;
WARN_ON
(
phymode
==
MODE_UNKNOWN
);
}
...
...
@@ -1159,15 +1177,15 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
rcu_read_unlock
();
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d up (associated) bssid %pM aid %d
\n
"
,
arvif
->
vdev_id
,
bss_conf
->
bssid
,
bss_conf
->
aid
);
ret
=
ath10k_wmi_vdev_up
(
ar
,
arvif
->
vdev_id
,
bss_conf
->
aid
,
bss_conf
->
bssid
);
if
(
ret
)
ath10k_warn
(
"VDEV: %d up failed: ret %d
\n
"
,
arvif
->
vdev_id
,
ret
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"VDEV: %d associated, BSSID: %pM, AID: %d
\n
"
,
arvif
->
vdev_id
,
bss_conf
->
bssid
,
bss_conf
->
aid
);
}
/*
...
...
@@ -1188,10 +1206,11 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
* No idea why this happens, even though VDEV-DOWN is supposed
* to be analogous to link down, so just stop the VDEV.
*/
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d stop (disassociated
\n
"
,
arvif
->
vdev_id
);
/* FIXME: check return value */
ret
=
ath10k_vdev_stop
(
arvif
);
if
(
!
ret
)
ath10k_dbg
(
ATH10K_DBG_MAC
,
"VDEV: %d stopped
\n
"
,
arvif
->
vdev_id
);
/*
* If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
...
...
@@ -1200,12 +1219,10 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
* interfaces as it expects there is no rx when no interface is
* running.
*/
ret
=
ath10k_wmi_vdev_down
(
ar
,
arvif
->
vdev_id
);
if
(
ret
)
ath10k_dbg
(
ATH10K_DBG_MAC
,
"VDEV: %d ath10k_wmi_vdev_down failed (%d)
\n
"
,
arvif
->
vdev_id
,
ret
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d down
\n
"
,
arvif
->
vdev_id
);
ath10k_wmi_flush_tx
(
ar
);
/* FIXME: why don't we print error if wmi call fails? */
ret
=
ath10k_wmi_vdev_down
(
ar
,
arvif
->
vdev_id
);
arvif
->
def_wep_key_index
=
0
;
}
...
...
@@ -1330,8 +1347,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
continue
;
ath10k_dbg
(
ATH10K_DBG_WMI
,
"
%s:
[%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d
\n
"
,
__func__
,
ch
-
arg
.
channels
,
arg
.
n_channels
,
"
mac channel
[%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d
\n
"
,
ch
-
arg
.
channels
,
arg
.
n_channels
,
ch
->
freq
,
ch
->
max_power
,
ch
->
max_reg_power
,
ch
->
max_antenna_gain
,
ch
->
mode
);
...
...
@@ -1431,7 +1448,8 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
if
(
key
->
keyidx
==
arvif
->
def_wep_key_index
)
return
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"new wep keyidx will be %d
\n
"
,
key
->
keyidx
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d keyidx %d
\n
"
,
arvif
->
vdev_id
,
key
->
keyidx
);
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_DEF_KEYID
,
...
...
@@ -1534,7 +1552,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
mutex_lock
(
&
ar
->
conf_mutex
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
processing
offchannel skb %p
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac
offchannel skb %p
\n
"
,
skb
);
hdr
=
(
struct
ieee80211_hdr
*
)
skb
->
data
;
...
...
@@ -1546,6 +1564,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
spin_unlock_bh
(
&
ar
->
data_lock
);
if
(
peer
)
/* FIXME: should this use ath10k_warn()? */
ath10k_dbg
(
ATH10K_DBG_MAC
,
"peer %pM on vdev %d already present
\n
"
,
peer_addr
,
vdev_id
);
...
...
@@ -1643,8 +1662,6 @@ static int ath10k_abort_scan(struct ath10k *ar)
return
-
EIO
;
}
ath10k_wmi_flush_tx
(
ar
);
ret
=
wait_for_completion_timeout
(
&
ar
->
scan
.
completed
,
3
*
HZ
);
if
(
ret
==
0
)
ath10k_warn
(
"timed out while waiting for scan to stop
\n
"
);
...
...
@@ -1678,10 +1695,6 @@ static int ath10k_start_scan(struct ath10k *ar,
if
(
ret
)
return
ret
;
/* make sure we submit the command so the completion
* timeout makes sense */
ath10k_wmi_flush_tx
(
ar
);
ret
=
wait_for_completion_timeout
(
&
ar
->
scan
.
started
,
1
*
HZ
);
if
(
ret
==
0
)
{
ath10k_abort_scan
(
ar
);
...
...
@@ -1744,7 +1757,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
ath10k_tx_h_seq_no
(
skb
);
}
memset
(
ATH10K_SKB_CB
(
skb
),
0
,
sizeof
(
*
ATH10K_SKB_CB
(
skb
)))
;
ATH10K_SKB_CB
(
skb
)
->
htt
.
is_offchan
=
false
;
ATH10K_SKB_CB
(
skb
)
->
htt
.
vdev_id
=
vdev_id
;
ATH10K_SKB_CB
(
skb
)
->
htt
.
tid
=
tid
;
...
...
@@ -1886,7 +1899,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock
(
&
ar
->
conf_mutex
);
if
(
changed
&
IEEE80211_CONF_CHANGE_CHANNEL
)
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
C
onfig channel %d mhz
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac c
onfig channel %d mhz
\n
"
,
conf
->
chandef
.
chan
->
center_freq
);
spin_lock_bh
(
&
ar
->
data_lock
);
ar
->
rx_channel
=
conf
->
chandef
.
chan
;
...
...
@@ -1903,7 +1916,6 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
ret
=
ath10k_monitor_destroy
(
ar
);
}
ath10k_wmi_flush_tx
(
ar
);
mutex_unlock
(
&
ar
->
conf_mutex
);
return
ret
;
}
...
...
@@ -1975,7 +1987,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
break
;
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
Add interface: id %d
type %d subtype %d
\n
"
,
ath10k_dbg
(
ATH10K_DBG_MAC
,
"
mac vdev create %d (add interface)
type %d subtype %d
\n
"
,
arvif
->
vdev_id
,
arvif
->
vdev_type
,
arvif
->
vdev_subtype
);
ret
=
ath10k_wmi_vdev_create
(
ar
,
arvif
->
vdev_id
,
arvif
->
vdev_type
,
...
...
@@ -2054,7 +2066,12 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
mutex_lock
(
&
ar
->
conf_mutex
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Remove interface: id %d
\n
"
,
arvif
->
vdev_id
);
spin_lock_bh
(
&
ar
->
data_lock
);
if
(
arvif
->
beacon
)
{
dev_kfree_skb_any
(
arvif
->
beacon
);
arvif
->
beacon
=
NULL
;
}
spin_unlock_bh
(
&
ar
->
data_lock
);
ar
->
free_vdev_map
|=
1
<<
(
arvif
->
vdev_id
);
...
...
@@ -2066,6 +2083,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
kfree
(
arvif
->
u
.
ap
.
noa_data
);
}
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev delete %d (remove interface)
\n
"
,
arvif
->
vdev_id
);
ret
=
ath10k_wmi_vdev_delete
(
ar
,
arvif
->
vdev_id
);
if
(
ret
)
ath10k_warn
(
"WMI vdev delete failed: %d
\n
"
,
ret
);
...
...
@@ -2107,18 +2127,20 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
if
((
ar
->
filter_flags
&
FIF_PROMISC_IN_BSS
)
&&
!
ar
->
monitor_enabled
)
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac monitor %d start
\n
"
,
ar
->
monitor_vdev_id
);
ret
=
ath10k_monitor_start
(
ar
,
ar
->
monitor_vdev_id
);
if
(
ret
)
ath10k_warn
(
"Unable to start monitor mode
\n
"
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Monitor mode started
\n
"
);
}
else
if
(
!
(
ar
->
filter_flags
&
FIF_PROMISC_IN_BSS
)
&&
ar
->
monitor_enabled
)
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac monitor %d stop
\n
"
,
ar
->
monitor_vdev_id
);
ret
=
ath10k_monitor_stop
(
ar
);
if
(
ret
)
ath10k_warn
(
"Unable to stop monitor mode
\n
"
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Monitor mode stopped
\n
"
);
}
mutex_unlock
(
&
ar
->
conf_mutex
);
...
...
@@ -2143,41 +2165,41 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_BEACON_INTERVAL
,
arvif
->
beacon_interval
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d beacon_interval %d
\n
"
,
arvif
->
vdev_id
,
arvif
->
beacon_interval
);
if
(
ret
)
ath10k_warn
(
"Failed to set beacon interval for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Beacon interval: %d set for VDEV: %d
\n
"
,
arvif
->
beacon_interval
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_BEACON
)
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"vdev %d set beacon tx mode to staggered
\n
"
,
arvif
->
vdev_id
);
ret
=
ath10k_wmi_pdev_set_param
(
ar
,
WMI_PDEV_PARAM_BEACON_TX_MODE
,
WMI_BEACON_STAGGERED_MODE
);
if
(
ret
)
ath10k_warn
(
"Failed to set beacon mode for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set staggered beacon mode for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_BEACON_INFO
)
{
arvif
->
dtim_period
=
info
->
dtim_period
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d dtim_period %d
\n
"
,
arvif
->
vdev_id
,
arvif
->
dtim_period
);
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_DTIM_PERIOD
,
arvif
->
dtim_period
);
if
(
ret
)
ath10k_warn
(
"Failed to set dtim period for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set dtim period: %d for VDEV: %d
\n
"
,
arvif
->
dtim_period
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_SSID
&&
...
...
@@ -2190,16 +2212,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
if
(
changed
&
BSS_CHANGED_BSSID
)
{
if
(
!
is_zero_ether_addr
(
info
->
bssid
))
{
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d create peer %pM
\n
"
,
arvif
->
vdev_id
,
info
->
bssid
);
ret
=
ath10k_peer_create
(
ar
,
arvif
->
vdev_id
,
info
->
bssid
);
if
(
ret
)
ath10k_warn
(
"Failed to add peer: %pM for VDEV: %d
\n
"
,
info
->
bssid
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Added peer: %pM for VDEV: %d
\n
"
,
info
->
bssid
,
arvif
->
vdev_id
);
if
(
vif
->
type
==
NL80211_IFTYPE_STATION
)
{
/*
...
...
@@ -2209,11 +2230,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
memcpy
(
arvif
->
u
.
sta
.
bssid
,
info
->
bssid
,
ETH_ALEN
);
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d start %pM
\n
"
,
arvif
->
vdev_id
,
info
->
bssid
);
/* FIXME: check return value */
ret
=
ath10k_vdev_start
(
arvif
);
if
(
!
ret
)
ath10k_dbg
(
ATH10K_DBG_MAC
,
"VDEV: %d started with BSSID: %pM
\n
"
,
arvif
->
vdev_id
,
info
->
bssid
);
}
/*
...
...
@@ -2237,16 +2259,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
cts_prot
=
0
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d cts_prot %d
\n
"
,
arvif
->
vdev_id
,
cts_prot
);
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_ENABLE_RTSCTS
,
cts_prot
);
if
(
ret
)
ath10k_warn
(
"Failed to set CTS prot for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set CTS prot: %d for VDEV: %d
\n
"
,
cts_prot
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_ERP_SLOT
)
{
...
...
@@ -2257,16 +2278,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
slottime
=
WMI_VDEV_SLOT_TIME_LONG
;
/* 20us */
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d slot_time %d
\n
"
,
arvif
->
vdev_id
,
slottime
);
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_SLOT_TIME
,
slottime
);
if
(
ret
)
ath10k_warn
(
"Failed to set erp slot for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set slottime: %d for VDEV: %d
\n
"
,
slottime
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_ERP_PREAMBLE
)
{
...
...
@@ -2276,16 +2296,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
else
preamble
=
WMI_VDEV_PREAMBLE_LONG
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d preamble %dn"
,
arvif
->
vdev_id
,
preamble
);
ret
=
ath10k_wmi_vdev_set_param
(
ar
,
arvif
->
vdev_id
,
WMI_VDEV_PARAM_PREAMBLE
,
preamble
);
if
(
ret
)
ath10k_warn
(
"Failed to set preamble for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set preamble: %d for VDEV: %d
\n
"
,
preamble
,
arvif
->
vdev_id
);
}
if
(
changed
&
BSS_CHANGED_ASSOC
)
{
...
...
@@ -2476,27 +2496,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* New station addition.
*/
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d peer create %pM (new sta)
\n
"
,
arvif
->
vdev_id
,
sta
->
addr
);
ret
=
ath10k_peer_create
(
ar
,
arvif
->
vdev_id
,
sta
->
addr
);
if
(
ret
)
ath10k_warn
(
"Failed to add peer: %pM for VDEV: %d
\n
"
,
sta
->
addr
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Added peer: %pM for VDEV: %d
\n
"
,
sta
->
addr
,
arvif
->
vdev_id
);
}
else
if
((
old_state
==
IEEE80211_STA_NONE
&&
new_state
==
IEEE80211_STA_NOTEXIST
))
{
/*
* Existing station deletion.
*/
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d peer delete %pM (sta gone)
\n
"
,
arvif
->
vdev_id
,
sta
->
addr
);
ret
=
ath10k_peer_delete
(
ar
,
arvif
->
vdev_id
,
sta
->
addr
);
if
(
ret
)
ath10k_warn
(
"Failed to delete peer: %pM for VDEV: %d
\n
"
,
sta
->
addr
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Removed peer: %pM for VDEV: %d
\n
"
,
sta
->
addr
,
arvif
->
vdev_id
);
if
(
vif
->
type
==
NL80211_IFTYPE_STATION
)
ath10k_bss_disassoc
(
hw
,
vif
);
...
...
@@ -2507,14 +2526,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* New association.
*/
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac sta %pM associated
\n
"
,
sta
->
addr
);
ret
=
ath10k_station_assoc
(
ar
,
arvif
,
sta
);
if
(
ret
)
ath10k_warn
(
"Failed to associate station: %pM
\n
"
,
sta
->
addr
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Station %pM moved to assoc state
\n
"
,
sta
->
addr
);
}
else
if
(
old_state
==
IEEE80211_STA_ASSOC
&&
new_state
==
IEEE80211_STA_AUTH
&&
(
vif
->
type
==
NL80211_IFTYPE_AP
||
...
...
@@ -2522,14 +2540,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
/*
* Disassociation.
*/
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac sta %pM disassociated
\n
"
,
sta
->
addr
);
ret
=
ath10k_station_disassoc
(
ar
,
arvif
,
sta
);
if
(
ret
)
ath10k_warn
(
"Failed to disassociate station: %pM
\n
"
,
sta
->
addr
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Station %pM moved to disassociated state
\n
"
,
sta
->
addr
);
}
mutex_unlock
(
&
ar
->
conf_mutex
);
...
...
@@ -2749,14 +2766,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
if
(
ar_iter
->
ar
->
state
==
ATH10K_STATE_RESTARTED
)
return
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d rts_threshold %d
\n
"
,
arvif
->
vdev_id
,
rts
);
ar_iter
->
ret
=
ath10k_mac_set_rts
(
arvif
,
rts
);
if
(
ar_iter
->
ret
)
ath10k_warn
(
"Failed to set RTS threshold for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set RTS threshold: %d for VDEV: %d
\n
"
,
rts
,
arvif
->
vdev_id
);
}
static
int
ath10k_set_rts_threshold
(
struct
ieee80211_hw
*
hw
,
u32
value
)
...
...
@@ -2791,14 +2807,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
if
(
ar_iter
->
ar
->
state
==
ATH10K_STATE_RESTARTED
)
return
;
ath10k_dbg
(
ATH10K_DBG_MAC
,
"mac vdev %d fragmentation_threshold %d
\n
"
,
arvif
->
vdev_id
,
frag
);
ar_iter
->
ret
=
ath10k_mac_set_frag
(
arvif
,
frag
);
if
(
ar_iter
->
ret
)
ath10k_warn
(
"Failed to set frag threshold for VDEV: %d
\n
"
,
arvif
->
vdev_id
);
else
ath10k_dbg
(
ATH10K_DBG_MAC
,
"Set frag threshold: %d for VDEV: %d
\n
"
,
frag
,
arvif
->
vdev_id
);
}
static
int
ath10k_set_frag_threshold
(
struct
ieee80211_hw
*
hw
,
u32
value
)
...
...
@@ -2838,8 +2853,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
bool
empty
;
spin_lock_bh
(
&
ar
->
htt
.
tx_lock
);
empty
=
bitmap_empty
(
ar
->
htt
.
used_msdu_ids
,
ar
->
htt
.
max_num_pending_tx
);
empty
=
(
ar
->
htt
.
num_pending_tx
==
0
);
spin_unlock_bh
(
&
ar
->
htt
.
tx_lock
);
skip
=
(
ar
->
state
==
ATH10K_STATE_WEDGED
);
...
...
@@ -3328,6 +3342,10 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_WANT_MONITOR_VIF
|
IEEE80211_HW_AP_LINK_PS
;
/* MSDU can have HTT TX fragment pushed in front. The additional 4
* bytes is used for padding/alignment if necessary. */
ar
->
hw
->
extra_tx_headroom
+=
sizeof
(
struct
htt_data_tx_desc_frag
)
*
2
+
4
;
if
(
ar
->
ht_cap_info
&
WMI_HT_CAP_DYNAMIC_SMPS
)
ar
->
hw
->
flags
|=
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS
;
...
...
drivers/net/wireless/ath/ath10k/pci.c
View file @
75ae83d6
...
...
@@ -612,31 +612,20 @@ struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info)
}
/* Called by lower (CE) layer when a send to Target completes. */
static
void
ath10k_pci_ce_send_done
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
ce_data
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
)
static
void
ath10k_pci_ce_send_done
(
struct
ath10k_ce_pipe
*
ce_state
)
{
struct
ath10k
*
ar
=
ce_state
->
ar
;
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
struct
ath10k_pci_pipe
*
pipe_info
=
&
ar_pci
->
pipe_info
[
ce_state
->
id
];
struct
ath10k_pci_compl
*
compl
;
bool
process
=
false
;
do
{
/*
* For the send completion of an item in sendlist, just
* increment num_sends_allowed. The upper layer callback will
* be triggered when last fragment is done with send.
*/
if
(
transfer_context
==
CE_SENDLIST_ITEM_CTXT
)
{
spin_lock_bh
(
&
pipe_info
->
pipe_lock
);
pipe_info
->
num_sends_allowed
++
;
spin_unlock_bh
(
&
pipe_info
->
pipe_lock
);
continue
;
}
void
*
transfer_context
;
u32
ce_data
;
unsigned
int
nbytes
;
unsigned
int
transfer_id
;
while
(
ath10k_ce_completed_send_next
(
ce_state
,
&
transfer_context
,
&
ce_data
,
&
nbytes
,
&
transfer_id
)
==
0
)
{
compl
=
get_free_compl
(
pipe_info
);
if
(
!
compl
)
break
;
...
...
@@ -655,38 +644,28 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state,
spin_lock_bh
(
&
ar_pci
->
compl_lock
);
list_add_tail
(
&
compl
->
list
,
&
ar_pci
->
compl_process
);
spin_unlock_bh
(
&
ar_pci
->
compl_lock
);
process
=
true
;
}
while
(
ath10k_ce_completed_send_next
(
ce_state
,
&
transfer_context
,
&
ce_data
,
&
nbytes
,
&
transfer_id
)
==
0
);
/*
* If only some of the items within a sendlist have completed,
* don't invoke completion processing until the entire sendlist
* has been sent.
*/
if
(
!
process
)
return
;
}
ath10k_pci_process_ce
(
ar
);
}
/* Called by lower (CE) layer when data is received from the Target. */
static
void
ath10k_pci_ce_recv_data
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
ce_data
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
,
unsigned
int
flags
)
static
void
ath10k_pci_ce_recv_data
(
struct
ath10k_ce_pipe
*
ce_state
)
{
struct
ath10k
*
ar
=
ce_state
->
ar
;
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
struct
ath10k_pci_pipe
*
pipe_info
=
&
ar_pci
->
pipe_info
[
ce_state
->
id
];
struct
ath10k_pci_compl
*
compl
;
struct
sk_buff
*
skb
;
void
*
transfer_context
;
u32
ce_data
;
unsigned
int
nbytes
;
unsigned
int
transfer_id
;
unsigned
int
flags
;
do
{
while
(
ath10k_ce_completed_recv_next
(
ce_state
,
&
transfer_context
,
&
ce_data
,
&
nbytes
,
&
transfer_id
,
&
flags
)
==
0
)
{
compl
=
get_free_compl
(
pipe_info
);
if
(
!
compl
)
break
;
...
...
@@ -709,12 +688,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state,
spin_lock_bh
(
&
ar_pci
->
compl_lock
);
list_add_tail
(
&
compl
->
list
,
&
ar_pci
->
compl_process
);
spin_unlock_bh
(
&
ar_pci
->
compl_lock
);
}
while
(
ath10k_ce_completed_recv_next
(
ce_state
,
&
transfer_context
,
&
ce_data
,
&
nbytes
,
&
transfer_id
,
&
flags
)
==
0
);
}
ath10k_pci_process_ce
(
ar
);
}
...
...
@@ -728,13 +702,10 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
struct
ath10k_pci
*
ar_pci
=
ath10k_pci_priv
(
ar
);
struct
ath10k_pci_pipe
*
pipe_info
=
&
(
ar_pci
->
pipe_info
[
pipe_id
]);
struct
ath10k_ce_pipe
*
ce_hdl
=
pipe_info
->
ce_hdl
;
struct
ce_sendlist
sendlist
;
unsigned
int
len
;
u32
flags
=
0
;
int
ret
;
memset
(
&
sendlist
,
0
,
sizeof
(
struct
ce_sendlist
));
len
=
min
(
bytes
,
nbuf
->
len
);
bytes
-=
len
;
...
...
@@ -749,8 +720,6 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
"ath10k tx: data: "
,
nbuf
->
data
,
nbuf
->
len
);
ath10k_ce_sendlist_buf_add
(
&
sendlist
,
skb_cb
->
paddr
,
len
,
flags
);
/* Make sure we have resources to handle this request */
spin_lock_bh
(
&
pipe_info
->
pipe_lock
);
if
(
!
pipe_info
->
num_sends_allowed
)
{
...
...
@@ -761,7 +730,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
pipe_info
->
num_sends_allowed
--
;
spin_unlock_bh
(
&
pipe_info
->
pipe_lock
);
ret
=
ath10k_ce_sendlist_send
(
ce_hdl
,
nbuf
,
&
sendlist
,
transfer_id
);
ret
=
ath10k_ce_sendlist_send
(
ce_hdl
,
nbuf
,
transfer_id
,
skb_cb
->
paddr
,
len
,
flags
);
if
(
ret
)
ath10k_warn
(
"CE send failed: %p
\n
"
,
nbuf
);
...
...
@@ -1316,15 +1286,14 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
while
(
ath10k_ce_cancel_send_next
(
ce_hdl
,
(
void
**
)
&
netbuf
,
&
ce_data
,
&
nbytes
,
&
id
)
==
0
)
{
if
(
netbuf
!=
CE_SENDLIST_ITEM_CTXT
)
/*
* Indicate the completion to higer layer to free
* the buffer
*/
ATH10K_SKB_CB
(
netbuf
)
->
is_aborted
=
true
;
ar_pci
->
msg_callbacks_current
.
tx_completion
(
ar
,
netbuf
,
id
);
/*
* Indicate the completion to higer layer to free
* the buffer
*/
ATH10K_SKB_CB
(
netbuf
)
->
is_aborted
=
true
;
ar_pci
->
msg_callbacks_current
.
tx_completion
(
ar
,
netbuf
,
id
);
}
}
...
...
@@ -1490,13 +1459,16 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
return
ret
;
}
static
void
ath10k_pci_bmi_send_done
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
data
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
)
static
void
ath10k_pci_bmi_send_done
(
struct
ath10k_ce_pipe
*
ce_state
)
{
struct
bmi_xfer
*
xfer
=
transfer_context
;
struct
bmi_xfer
*
xfer
;
u32
ce_data
;
unsigned
int
nbytes
;
unsigned
int
transfer_id
;
if
(
ath10k_ce_completed_send_next
(
ce_state
,
(
void
**
)
&
xfer
,
&
ce_data
,
&
nbytes
,
&
transfer_id
))
return
;
if
(
xfer
->
wait_for_resp
)
return
;
...
...
@@ -1504,14 +1476,17 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state,
complete
(
&
xfer
->
done
);
}
static
void
ath10k_pci_bmi_recv_data
(
struct
ath10k_ce_pipe
*
ce_state
,
void
*
transfer_context
,
u32
data
,
unsigned
int
nbytes
,
unsigned
int
transfer_id
,
unsigned
int
flags
)
static
void
ath10k_pci_bmi_recv_data
(
struct
ath10k_ce_pipe
*
ce_state
)
{
struct
bmi_xfer
*
xfer
=
transfer_context
;
struct
bmi_xfer
*
xfer
;
u32
ce_data
;
unsigned
int
nbytes
;
unsigned
int
transfer_id
;
unsigned
int
flags
;
if
(
ath10k_ce_completed_recv_next
(
ce_state
,
(
void
**
)
&
xfer
,
&
ce_data
,
&
nbytes
,
&
transfer_id
,
&
flags
))
return
;
if
(
!
xfer
->
wait_for_resp
)
{
ath10k_warn
(
"unexpected: BMI data received; ignoring
\n
"
);
...
...
@@ -2374,10 +2349,10 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
switch
(
i
)
{
case
ATH10K_PCI_FEATURE_MSI_X
:
ath10k_dbg
(
ATH10K_DBG_
PCI
,
"device supports MSI-X
\n
"
);
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"device supports MSI-X
\n
"
);
break
;
case
ATH10K_PCI_FEATURE_SOC_POWER_SAVE
:
ath10k_dbg
(
ATH10K_DBG_
PCI
,
"QCA98XX SoC power save enabled
\n
"
);
ath10k_dbg
(
ATH10K_DBG_
BOOT
,
"QCA98XX SoC power save enabled
\n
"
);
break
;
}
}
...
...
@@ -2503,6 +2478,8 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ath10k_do_pci_sleep
(
ar
);
ath10k_dbg
(
ATH10K_DBG_BOOT
,
"boot pci_mem 0x%p
\n
"
,
ar_pci
->
mem
);
ret
=
ath10k_core_register
(
ar
,
chip_id
);
if
(
ret
)
{
ath10k_err
(
"could not register driver core (%d)
\n
"
,
ret
);
...
...
drivers/net/wireless/ath/ath10k/rx_desc.h
View file @
75ae83d6
...
...
@@ -422,10 +422,30 @@ struct rx_mpdu_end {
#define RX_MSDU_START_INFO1_IP_FRAG (1 << 14)
#define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15)
/* The decapped header (rx_hdr_status) contains the following:
* a) 802.11 header
* [padding to 4 bytes]
* b) HW crypto parameter
* - 0 bytes for no security
* - 4 bytes for WEP
* - 8 bytes for TKIP, AES
* [padding to 4 bytes]
* c) A-MSDU subframe header (14 bytes) if appliable
* d) LLC/SNAP (RFC1042, 8 bytes)
*
* In case of A-MSDU only first frame in sequence contains (a) and (b). */
enum
rx_msdu_decap_format
{
RX_MSDU_DECAP_RAW
=
0
,
RX_MSDU_DECAP_NATIVE_WIFI
=
1
,
RX_MSDU_DECAP_RAW
=
0
,
/* Note: QoS frames are reported as non-QoS. The rx_hdr_status in
* htt_rx_desc contains the original decapped 802.11 header. */
RX_MSDU_DECAP_NATIVE_WIFI
=
1
,
/* Payload contains an ethernet header (struct ethhdr). */
RX_MSDU_DECAP_ETHERNET2_DIX
=
2
,
/* Payload contains two 48-bit addresses and 2-byte length (14 bytes
* total), followed by an RFC1042 header (8 bytes). */
RX_MSDU_DECAP_8023_SNAP_LLC
=
3
};
...
...
drivers/net/wireless/ath/ath10k/trace.h
View file @
75ae83d6
...
...
@@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump,
);
TRACE_EVENT
(
ath10k_wmi_cmd
,
TP_PROTO
(
int
id
,
void
*
buf
,
size_t
buf_len
),
TP_PROTO
(
int
id
,
void
*
buf
,
size_t
buf_len
,
int
ret
),
TP_ARGS
(
id
,
buf
,
buf_len
),
TP_ARGS
(
id
,
buf
,
buf_len
,
ret
),
TP_STRUCT__entry
(
__field
(
unsigned
int
,
id
)
__field
(
size_t
,
buf_len
)
__dynamic_array
(
u8
,
buf
,
buf_len
)
__field
(
int
,
ret
)
),
TP_fast_assign
(
__entry
->
id
=
id
;
__entry
->
buf_len
=
buf_len
;
__entry
->
ret
=
ret
;
memcpy
(
__get_dynamic_array
(
buf
),
buf
,
buf_len
);
),
TP_printk
(
"id %d len %zu"
,
"id %d len %zu
ret %d
"
,
__entry
->
id
,
__entry
->
buf_len
__entry
->
buf_len
,
__entry
->
ret
)
);
...
...
@@ -158,6 +161,27 @@ TRACE_EVENT(ath10k_wmi_event,
)
);
TRACE_EVENT
(
ath10k_htt_stats
,
TP_PROTO
(
void
*
buf
,
size_t
buf_len
),
TP_ARGS
(
buf
,
buf_len
),
TP_STRUCT__entry
(
__field
(
size_t
,
buf_len
)
__dynamic_array
(
u8
,
buf
,
buf_len
)
),
TP_fast_assign
(
__entry
->
buf_len
=
buf_len
;
memcpy
(
__get_dynamic_array
(
buf
),
buf
,
buf_len
);
),
TP_printk
(
"len %zu"
,
__entry
->
buf_len
)
);
#endif
/* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */
...
...
drivers/net/wireless/ath/ath10k/txrx.c
View file @
75ae83d6
...
...
@@ -44,40 +44,39 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
spin_unlock_bh
(
&
ar
->
data_lock
);
}
void
ath10k_txrx_tx_unref
(
struct
ath10k_htt
*
htt
,
struct
sk_buff
*
txdesc
)
void
ath10k_txrx_tx_unref
(
struct
ath10k_htt
*
htt
,
const
struct
htt_tx_done
*
tx_done
)
{
struct
device
*
dev
=
htt
->
ar
->
dev
;
struct
ieee80211_tx_info
*
info
;
struct
sk_buff
*
txfrag
=
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
txfrag
;
struct
sk_buff
*
msdu
=
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
msdu
;
struct
ath10k_skb_cb
*
skb_cb
;
struct
sk_buff
*
msdu
;
int
ret
;
if
(
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
refcount
==
0
)
return
;
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
refcount
--
;
ath10k_dbg
(
ATH10K_DBG_HTT
,
"htt tx completion msdu_id %u discard %d no_ack %d
\n
"
,
tx_done
->
msdu_id
,
!!
tx_done
->
discard
,
!!
tx_done
->
no_ack
);
if
(
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
refcount
>
0
)
if
(
tx_done
->
msdu_id
>=
htt
->
max_num_pending_tx
)
{
ath10k_warn
(
"warning: msdu_id %d too big, ignoring
\n
"
,
tx_done
->
msdu_id
);
return
;
if
(
txfrag
)
{
ret
=
ath10k_skb_unmap
(
dev
,
txfrag
);
if
(
ret
)
ath10k_warn
(
"txfrag unmap failed (%d)
\n
"
,
ret
);
dev_kfree_skb_any
(
txfrag
);
}
msdu
=
htt
->
pending_tx
[
tx_done
->
msdu_id
];
skb_cb
=
ATH10K_SKB_CB
(
msdu
);
ret
=
ath10k_skb_unmap
(
dev
,
msdu
);
if
(
ret
)
ath10k_warn
(
"data skb unmap failed (%d)
\n
"
,
ret
);
if
(
skb_cb
->
htt
.
frag_len
)
skb_pull
(
msdu
,
skb_cb
->
htt
.
frag_len
+
skb_cb
->
htt
.
pad_len
);
ath10k_report_offchan_tx
(
htt
->
ar
,
msdu
);
info
=
IEEE80211_SKB_CB
(
msdu
);
memset
(
&
info
->
status
,
0
,
sizeof
(
info
->
status
));
if
(
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
discard
)
{
if
(
tx_done
->
discard
)
{
ieee80211_free_txskb
(
htt
->
ar
->
hw
,
msdu
);
goto
exit
;
}
...
...
@@ -85,7 +84,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
if
(
!
(
info
->
flags
&
IEEE80211_TX_CTL_NO_ACK
))
info
->
flags
|=
IEEE80211_TX_STAT_ACK
;
if
(
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
no_ack
)
if
(
tx_done
->
no_ack
)
info
->
flags
&=
~
IEEE80211_TX_STAT_ACK
;
ieee80211_tx_status
(
htt
->
ar
->
hw
,
msdu
);
...
...
@@ -93,36 +92,12 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
exit:
spin_lock_bh
(
&
htt
->
tx_lock
);
htt
->
pending_tx
[
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
msdu_id
);
htt
->
pending_tx
[
tx_done
->
msdu_id
]
=
NULL
;
ath10k_htt_tx_free_msdu_id
(
htt
,
tx_done
->
msdu_id
);
__ath10k_htt_tx_dec_pending
(
htt
);
if
(
bitmap_empty
(
htt
->
used_msdu_ids
,
htt
->
max_num_pending_tx
)
)
if
(
htt
->
num_pending_tx
==
0
)
wake_up
(
&
htt
->
empty_tx_wq
);
spin_unlock_bh
(
&
htt
->
tx_lock
);
dev_kfree_skb_any
(
txdesc
);
}
void
ath10k_txrx_tx_completed
(
struct
ath10k_htt
*
htt
,
const
struct
htt_tx_done
*
tx_done
)
{
struct
sk_buff
*
txdesc
;
ath10k_dbg
(
ATH10K_DBG_HTT
,
"htt tx completion msdu_id %u discard %d no_ack %d
\n
"
,
tx_done
->
msdu_id
,
!!
tx_done
->
discard
,
!!
tx_done
->
no_ack
);
if
(
tx_done
->
msdu_id
>=
htt
->
max_num_pending_tx
)
{
ath10k_warn
(
"warning: msdu_id %d too big, ignoring
\n
"
,
tx_done
->
msdu_id
);
return
;
}
txdesc
=
htt
->
pending_tx
[
tx_done
->
msdu_id
];
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
discard
=
tx_done
->
discard
;
ATH10K_SKB_CB
(
txdesc
)
->
htt
.
no_ack
=
tx_done
->
no_ack
;
ath10k_txrx_tx_unref
(
htt
,
txdesc
);
}
static
const
u8
rx_legacy_rate_idx
[]
=
{
...
...
@@ -293,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
status
->
vht_nss
,
status
->
freq
,
status
->
band
);
ath10k_dbg_dump
(
ATH10K_DBG_HTT_DUMP
,
NULL
,
"rx skb: "
,
info
->
skb
->
data
,
info
->
skb
->
len
);
ieee80211_rx
(
ar
->
hw
,
info
->
skb
);
}
...
...
drivers/net/wireless/ath/ath10k/txrx.h
View file @
75ae83d6
...
...
@@ -19,9 +19,8 @@
#include "htt.h"
void
ath10k_txrx_tx_unref
(
struct
ath10k_htt
*
htt
,
struct
sk_buff
*
txdesc
);
void
ath10k_txrx_tx_completed
(
struct
ath10k_htt
*
htt
,
const
struct
htt_tx_done
*
tx_done
);
void
ath10k_txrx_tx_unref
(
struct
ath10k_htt
*
htt
,
const
struct
htt_tx_done
*
tx_done
);
void
ath10k_process_rx
(
struct
ath10k
*
ar
,
struct
htt_rx_info
*
info
);
struct
ath10k_peer
*
ath10k_peer_find
(
struct
ath10k
*
ar
,
int
vdev_id
,
...
...
drivers/net/wireless/ath/ath10k/wmi.c
View file @
75ae83d6
...
...
@@ -23,30 +23,6 @@
#include "wmi.h"
#include "mac.h"
void
ath10k_wmi_flush_tx
(
struct
ath10k
*
ar
)
{
int
ret
;
lockdep_assert_held
(
&
ar
->
conf_mutex
);
if
(
ar
->
state
==
ATH10K_STATE_WEDGED
)
{
ath10k_warn
(
"wmi flush skipped - device is wedged anyway
\n
"
);
return
;
}
ret
=
wait_event_timeout
(
ar
->
wmi
.
wq
,
atomic_read
(
&
ar
->
wmi
.
pending_tx_count
)
==
0
,
5
*
HZ
);
if
(
atomic_read
(
&
ar
->
wmi
.
pending_tx_count
)
==
0
)
return
;
if
(
ret
==
0
)
ret
=
-
ETIMEDOUT
;
if
(
ret
<
0
)
ath10k_warn
(
"wmi flush failed (%d)
\n
"
,
ret
);
}
int
ath10k_wmi_wait_for_service_ready
(
struct
ath10k
*
ar
)
{
int
ret
;
...
...
@@ -85,18 +61,14 @@ static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
static
void
ath10k_wmi_htc_tx_complete
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
{
dev_kfree_skb
(
skb
);
if
(
atomic_sub_return
(
1
,
&
ar
->
wmi
.
pending_tx_count
)
==
0
)
wake_up
(
&
ar
->
wmi
.
wq
);
}
/* WMI command API */
static
int
ath10k_wmi_cmd_send
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
,
enum
wmi_cmd_id
cmd_id
)
static
int
ath10k_wmi_cmd_send_nowait
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
,
enum
wmi_cmd_id
cmd_id
)
{
struct
ath10k_skb_cb
*
skb_cb
=
ATH10K_SKB_CB
(
skb
);
struct
wmi_cmd_hdr
*
cmd_hdr
;
int
status
;
int
ret
;
u32
cmd
=
0
;
if
(
skb_push
(
skb
,
sizeof
(
struct
wmi_cmd_hdr
))
==
NULL
)
...
...
@@ -107,26 +79,87 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
cmd_hdr
=
(
struct
wmi_cmd_hdr
*
)
skb
->
data
;
cmd_hdr
->
cmd_id
=
__cpu_to_le32
(
cmd
);
if
(
atomic_add_return
(
1
,
&
ar
->
wmi
.
pending_tx_count
)
>
WMI_MAX_PENDING_TX_COUNT
)
{
/* avoid using up memory when FW hangs */
dev_kfree_skb
(
skb
);
atomic_dec
(
&
ar
->
wmi
.
pending_tx_count
);
return
-
EBUSY
;
}
memset
(
skb_cb
,
0
,
sizeof
(
*
skb_cb
));
ret
=
ath10k_htc_send
(
&
ar
->
htc
,
ar
->
wmi
.
eid
,
skb
);
trace_ath10k_wmi_cmd
(
cmd_id
,
skb
->
data
,
skb
->
len
,
ret
);
trace_ath10k_wmi_cmd
(
cmd_id
,
skb
->
data
,
skb
->
len
);
if
(
ret
)
goto
err_pull
;
status
=
ath10k_htc_send
(
&
ar
->
htc
,
ar
->
wmi
.
eid
,
skb
);
if
(
status
)
{
return
0
;
err_pull:
skb_pull
(
skb
,
sizeof
(
struct
wmi_cmd_hdr
));
return
ret
;
}
static
void
ath10k_wmi_tx_beacon_nowait
(
struct
ath10k_vif
*
arvif
)
{
struct
wmi_bcn_tx_arg
arg
=
{
0
};
int
ret
;
lockdep_assert_held
(
&
arvif
->
ar
->
data_lock
);
if
(
arvif
->
beacon
==
NULL
)
return
;
arg
.
vdev_id
=
arvif
->
vdev_id
;
arg
.
tx_rate
=
0
;
arg
.
tx_power
=
0
;
arg
.
bcn
=
arvif
->
beacon
->
data
;
arg
.
bcn_len
=
arvif
->
beacon
->
len
;
ret
=
ath10k_wmi_beacon_send_nowait
(
arvif
->
ar
,
&
arg
);
if
(
ret
)
return
;
dev_kfree_skb_any
(
arvif
->
beacon
);
arvif
->
beacon
=
NULL
;
}
static
void
ath10k_wmi_tx_beacons_iter
(
void
*
data
,
u8
*
mac
,
struct
ieee80211_vif
*
vif
)
{
struct
ath10k_vif
*
arvif
=
ath10k_vif_to_arvif
(
vif
);
ath10k_wmi_tx_beacon_nowait
(
arvif
);
}
static
void
ath10k_wmi_tx_beacons_nowait
(
struct
ath10k
*
ar
)
{
spin_lock_bh
(
&
ar
->
data_lock
);
ieee80211_iterate_active_interfaces_atomic
(
ar
->
hw
,
IEEE80211_IFACE_ITER_NORMAL
,
ath10k_wmi_tx_beacons_iter
,
NULL
);
spin_unlock_bh
(
&
ar
->
data_lock
);
}
static
void
ath10k_wmi_op_ep_tx_credits
(
struct
ath10k
*
ar
)
{
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait
(
ar
);
wake_up
(
&
ar
->
wmi
.
tx_credits_wq
);
}
static
int
ath10k_wmi_cmd_send
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
,
enum
wmi_cmd_id
cmd_id
)
{
int
ret
=
-
EINVAL
;
wait_event_timeout
(
ar
->
wmi
.
tx_credits_wq
,
({
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait
(
ar
);
ret
=
ath10k_wmi_cmd_send_nowait
(
ar
,
skb
,
cmd_id
);
(
ret
!=
-
EAGAIN
);
}),
3
*
HZ
);
if
(
ret
)
dev_kfree_skb_any
(
skb
);
atomic_dec
(
&
ar
->
wmi
.
pending_tx_count
);
return
status
;
}
return
0
;
return
ret
;
}
static
int
ath10k_wmi_event_scan
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
...
...
@@ -748,10 +781,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
int
i
=
-
1
;
struct
wmi_bcn_info
*
bcn_info
;
struct
ath10k_vif
*
arvif
;
struct
wmi_bcn_tx_arg
arg
;
struct
sk_buff
*
bcn
;
int
vdev_id
=
0
;
int
ret
;
ath10k_dbg
(
ATH10K_DBG_MGMT
,
"WMI_HOST_SWBA_EVENTID
\n
"
);
...
...
@@ -808,17 +839,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_update_tim
(
ar
,
arvif
,
bcn
,
bcn_info
);
ath10k_wmi_update_noa
(
ar
,
arvif
,
bcn
,
bcn_info
);
arg
.
vdev_id
=
arvif
->
vdev_id
;
arg
.
tx_rate
=
0
;
arg
.
tx_power
=
0
;
arg
.
bcn
=
bcn
->
data
;
arg
.
bcn_len
=
bcn
->
len
;
spin_lock_bh
(
&
ar
->
data_lock
);
if
(
arvif
->
beacon
)
{
ath10k_warn
(
"SWBA overrun on vdev %d
\n
"
,
arvif
->
vdev_id
);
dev_kfree_skb_any
(
arvif
->
beacon
);
}
ret
=
ath10k_wmi_beacon_send
(
ar
,
&
arg
);
if
(
ret
)
ath10k_warn
(
"could not send beacon (%d)
\n
"
,
ret
);
arvif
->
beacon
=
bcn
;
dev_kfree_skb_any
(
bcn
);
ath10k_wmi_tx_beacon_nowait
(
arvif
);
spin_unlock_bh
(
&
ar
->
data_lock
);
}
}
...
...
@@ -1024,7 +1055,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
return
0
;
}
static
void
ath10k_wmi_
event_process
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
static
void
ath10k_wmi_
process_rx
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
{
struct
wmi_cmd_hdr
*
cmd_hdr
;
enum
wmi_event_id
id
;
...
...
@@ -1143,64 +1174,18 @@ static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
dev_kfree_skb
(
skb
);
}
static
void
ath10k_wmi_event_work
(
struct
work_struct
*
work
)
{
struct
ath10k
*
ar
=
container_of
(
work
,
struct
ath10k
,
wmi
.
wmi_event_work
);
struct
sk_buff
*
skb
;
for
(;;)
{
skb
=
skb_dequeue
(
&
ar
->
wmi
.
wmi_event_list
);
if
(
!
skb
)
break
;
ath10k_wmi_event_process
(
ar
,
skb
);
}
}
static
void
ath10k_wmi_process_rx
(
struct
ath10k
*
ar
,
struct
sk_buff
*
skb
)
{
struct
wmi_cmd_hdr
*
cmd_hdr
=
(
struct
wmi_cmd_hdr
*
)
skb
->
data
;
enum
wmi_event_id
event_id
;
event_id
=
MS
(
__le32_to_cpu
(
cmd_hdr
->
cmd_id
),
WMI_CMD_HDR_CMD_ID
);
/* some events require to be handled ASAP
* thus can't be defered to a worker thread */
switch
(
event_id
)
{
case
WMI_HOST_SWBA_EVENTID
:
case
WMI_MGMT_RX_EVENTID
:
ath10k_wmi_event_process
(
ar
,
skb
);
return
;
default:
break
;
}
skb_queue_tail
(
&
ar
->
wmi
.
wmi_event_list
,
skb
);
queue_work
(
ar
->
workqueue
,
&
ar
->
wmi
.
wmi_event_work
);
}
/* WMI Initialization functions */
int
ath10k_wmi_attach
(
struct
ath10k
*
ar
)
{
init_completion
(
&
ar
->
wmi
.
service_ready
);
init_completion
(
&
ar
->
wmi
.
unified_ready
);
init_waitqueue_head
(
&
ar
->
wmi
.
wq
);
skb_queue_head_init
(
&
ar
->
wmi
.
wmi_event_list
);
INIT_WORK
(
&
ar
->
wmi
.
wmi_event_work
,
ath10k_wmi_event_work
);
init_waitqueue_head
(
&
ar
->
wmi
.
tx_credits_wq
);
return
0
;
}
void
ath10k_wmi_detach
(
struct
ath10k
*
ar
)
{
/* HTC should've drained the packets already */
if
(
WARN_ON
(
atomic_read
(
&
ar
->
wmi
.
pending_tx_count
)
>
0
))
ath10k_warn
(
"there are still pending packets
\n
"
);
cancel_work_sync
(
&
ar
->
wmi
.
wmi_event_work
);
skb_queue_purge
(
&
ar
->
wmi
.
wmi_event_list
);
}
int
ath10k_wmi_connect_htc_service
(
struct
ath10k
*
ar
)
...
...
@@ -1215,6 +1200,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
/* these fields are the same for all service endpoints */
conn_req
.
ep_ops
.
ep_tx_complete
=
ath10k_wmi_htc_tx_complete
;
conn_req
.
ep_ops
.
ep_rx_complete
=
ath10k_wmi_process_rx
;
conn_req
.
ep_ops
.
ep_tx_credits
=
ath10k_wmi_op_ep_tx_credits
;
/* connect to control service */
conn_req
.
service_id
=
ATH10K_HTC_SVC_ID_WMI_CONTROL
;
...
...
@@ -2125,7 +2111,8 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
return
ath10k_wmi_cmd_send
(
ar
,
skb
,
WMI_PEER_ASSOC_CMDID
);
}
int
ath10k_wmi_beacon_send
(
struct
ath10k
*
ar
,
const
struct
wmi_bcn_tx_arg
*
arg
)
int
ath10k_wmi_beacon_send_nowait
(
struct
ath10k
*
ar
,
const
struct
wmi_bcn_tx_arg
*
arg
)
{
struct
wmi_bcn_tx_cmd
*
cmd
;
struct
sk_buff
*
skb
;
...
...
@@ -2141,7 +2128,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
cmd
->
hdr
.
bcn_len
=
__cpu_to_le32
(
arg
->
bcn_len
);
memcpy
(
cmd
->
bcn
,
arg
->
bcn
,
arg
->
bcn_len
);
return
ath10k_wmi_cmd_send
(
ar
,
skb
,
WMI_BCN_TX_CMDID
);
return
ath10k_wmi_cmd_send
_nowait
(
ar
,
skb
,
WMI_BCN_TX_CMDID
);
}
static
void
ath10k_wmi_pdev_set_wmm_param
(
struct
wmi_wmm_params
*
params
,
...
...
drivers/net/wireless/ath/ath10k/wmi.h
View file @
75ae83d6
...
...
@@ -508,6 +508,48 @@ enum wmi_phy_mode {
MODE_MAX
=
14
};
static
inline
const
char
*
ath10k_wmi_phymode_str
(
enum
wmi_phy_mode
mode
)
{
switch
(
mode
)
{
case
MODE_11A
:
return
"11a"
;
case
MODE_11G
:
return
"11g"
;
case
MODE_11B
:
return
"11b"
;
case
MODE_11GONLY
:
return
"11gonly"
;
case
MODE_11NA_HT20
:
return
"11na-ht20"
;
case
MODE_11NG_HT20
:
return
"11ng-ht20"
;
case
MODE_11NA_HT40
:
return
"11na-ht40"
;
case
MODE_11NG_HT40
:
return
"11ng-ht40"
;
case
MODE_11AC_VHT20
:
return
"11ac-vht20"
;
case
MODE_11AC_VHT40
:
return
"11ac-vht40"
;
case
MODE_11AC_VHT80
:
return
"11ac-vht80"
;
case
MODE_11AC_VHT20_2G
:
return
"11ac-vht20-2g"
;
case
MODE_11AC_VHT40_2G
:
return
"11ac-vht40-2g"
;
case
MODE_11AC_VHT80_2G
:
return
"11ac-vht80-2g"
;
case
MODE_UNKNOWN
:
/* skip */
break
;
/* no default handler to allow compiler to check that the
* enum is fully handled */
};
return
"<unknown>"
;
}
#define WMI_CHAN_LIST_TAG 0x1
#define WMI_SSID_LIST_TAG 0x2
#define WMI_BSSID_LIST_TAG 0x3
...
...
@@ -763,14 +805,6 @@ struct wmi_service_ready_event {
struct
wlan_host_mem_req
mem_reqs
[
1
];
}
__packed
;
/*
* status consists of upper 16 bits fo int status and lower 16 bits of
* module ID that retuned status
*/
#define WLAN_INIT_STATUS_SUCCESS 0x0
#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff)
#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
...
...
@@ -3010,7 +3044,6 @@ struct wmi_force_fw_hang_cmd {
#define WMI_MAX_EVENT 0x1000
/* Maximum number of pending TXed WMI packets */
#define WMI_MAX_PENDING_TX_COUNT 128
#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
/* By default disable power save for IBSS */
...
...
@@ -3023,7 +3056,6 @@ int ath10k_wmi_attach(struct ath10k *ar);
void
ath10k_wmi_detach
(
struct
ath10k
*
ar
);
int
ath10k_wmi_wait_for_service_ready
(
struct
ath10k
*
ar
);
int
ath10k_wmi_wait_for_unified_ready
(
struct
ath10k
*
ar
);
void
ath10k_wmi_flush_tx
(
struct
ath10k
*
ar
);
int
ath10k_wmi_connect_htc_service
(
struct
ath10k
*
ar
);
int
ath10k_wmi_pdev_set_channel
(
struct
ath10k
*
ar
,
...
...
@@ -3076,7 +3108,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
enum
wmi_ap_ps_peer_param
param_id
,
u32
value
);
int
ath10k_wmi_scan_chan_list
(
struct
ath10k
*
ar
,
const
struct
wmi_scan_chan_list_arg
*
arg
);
int
ath10k_wmi_beacon_send
(
struct
ath10k
*
ar
,
const
struct
wmi_bcn_tx_arg
*
arg
);
int
ath10k_wmi_beacon_send_nowait
(
struct
ath10k
*
ar
,
const
struct
wmi_bcn_tx_arg
*
arg
);
int
ath10k_wmi_pdev_set_wmm_params
(
struct
ath10k
*
ar
,
const
struct
wmi_pdev_set_wmm_params_arg
*
arg
);
int
ath10k_wmi_request_stats
(
struct
ath10k
*
ar
,
enum
wmi_stats_id
stats_id
);
...
...
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