* Re: IPv6-UDP 0x0000 checksum
From: Johannes Berg @ 2017-01-26 15:36 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, linux-wireless
In-Reply-To: <1485444476.5145.136.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2017-01-26 at 07:27 -0800, Eric Dumazet wrote:
>
> if (!uh->check && !udp_sk(sk)->no_check6_rx) {
> udp6_csum_zero_error(skb);
> goto csum_error;
> }
Yeah, I'd found no_check6_rx already, was still trying to figure out this one :)
Looks like we actually check uh->check regardless of anything the
driver said (CHECKSUM_UNNECESSARY, or whatever), so we should be fine
even with the hardware tagging it as good in this case.
johannes
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Johannes Berg @ 2017-01-26 15:32 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, linux-wireless
In-Reply-To: <1485444276.5145.133.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2017-01-26 at 07:24 -0800, Eric Dumazet wrote:
> On Thu, 2017-01-26 at 15:49 +0100, Johannes Berg wrote:
>
> > Unfortunately, I haven't been able to actually test this yet. I
> > also
> > didn't find the code that would drop frames with CSUM 0 either, so
> > I'm
> > thinking - for now - that if all the csum handling is skipped,
> > dropping
> > 0 csum frames would also be, and then we'd accept a frame we should
> > actually have dropped.
> >
> > I'll go test this I guess :)
> >
> > Any pointers to where 0 csum frames are dropped?
>
> Probably in udp6_csum_init()
Well, now that I see that, I see that they're actually valid in some
circumstances. Oops. :)
Will need to revisit, and check how we set no_check6_rx, etc.
johannes
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Eric Dumazet @ 2017-01-26 15:27 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <1485444276.5145.133.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2017-01-26 at 07:24 -0800, Eric Dumazet wrote:
> On Thu, 2017-01-26 at 15:49 +0100, Johannes Berg wrote:
>
> > Unfortunately, I haven't been able to actually test this yet. I also
> > didn't find the code that would drop frames with CSUM 0 either, so I'm
> > thinking - for now - that if all the csum handling is skipped, dropping
> > 0 csum frames would also be, and then we'd accept a frame we should
> > actually have dropped.
> >
> > I'll go test this I guess :)
> >
> > Any pointers to where 0 csum frames are dropped?
>
> Probably in udp6_csum_init()
vi +804 net/ipv6/udp.c
if (!uh->check && !udp_sk(sk)->no_check6_rx) {
udp6_csum_zero_error(skb);
goto csum_error;
}
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Eric Dumazet @ 2017-01-26 15:24 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <1485442164.14760.11.camel@sipsolutions.net>
On Thu, 2017-01-26 at 15:49 +0100, Johannes Berg wrote:
> Unfortunately, I haven't been able to actually test this yet. I also
> didn't find the code that would drop frames with CSUM 0 either, so I'm
> thinking - for now - that if all the csum handling is skipped, dropping
> 0 csum frames would also be, and then we'd accept a frame we should
> actually have dropped.
>
> I'll go test this I guess :)
>
> Any pointers to where 0 csum frames are dropped?
Probably in udp6_csum_init()
^ permalink raw reply
* [PATCH] wlcore: print the sdio buffer after reading it
From: Guy Mishol @ 2017-01-26 15:35 UTC (permalink / raw)
To: linux-wireless; +Cc: Guy Mishol
fix an issue where we printed the sdio buffer before
actually read it.
Signed-off-by: Guy Mishol <guym@ti.com>
---
drivers/net/wireless/ti/wlcore/sdio.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c
index 47fe7f9..287023e 100644
--- a/drivers/net/wireless/ti/wlcore/sdio.c
+++ b/drivers/net/wireless/ti/wlcore/sdio.c
@@ -81,13 +81,6 @@ static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr,
sdio_claim_host(func);
- if (unlikely(dump)) {
- printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
- print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
- DUMP_PREFIX_OFFSET, 16, 1,
- buf, len, false);
- }
-
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
@@ -107,6 +100,13 @@ static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr,
if (WARN_ON(ret))
dev_err(child->parent, "sdio read failed (%d)\n", ret);
+ if (unlikely(dump)) {
+ printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
+ print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ buf, len, false);
+ }
+
return ret;
}
--
2.6.4
^ permalink raw reply related
* Re: IPv6-UDP 0x0000 checksum
From: Johannes Berg @ 2017-01-26 14:49 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, linux-wireless
In-Reply-To: <1485441942.5145.131.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2017-01-26 at 06:45 -0800, Eric Dumazet wrote:
> On Thu, 2017-01-26 at 14:49 +0100, Johannes Berg wrote:
>
> > Oops, sorry - receive. We can only indicate "CHECKSUM_UNNECESSARY",
> > nothing more advanced right now, but right now we'd indicate that
> > if
> > the packet had 0x0000 in the checksum field, but should've had
> > 0xffff.
> >
> > On TX I believe we actually do in HW exactly what your patch just
> > did.
>
> Can you describe the visible effects of this problem ?
>
> Is that because of a conversion we might do later to
> CHECKSUM_COMPLETE ?
Unfortunately, I haven't been able to actually test this yet. I also
didn't find the code that would drop frames with CSUM 0 either, so I'm
thinking - for now - that if all the csum handling is skipped, dropping
0 csum frames would also be, and then we'd accept a frame we should
actually have dropped.
I'll go test this I guess :)
Any pointers to where 0 csum frames are dropped?
johannes
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Eric Dumazet @ 2017-01-26 14:45 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <1485438546.14760.7.camel@sipsolutions.net>
On Thu, 2017-01-26 at 14:49 +0100, Johannes Berg wrote:
> Oops, sorry - receive. We can only indicate "CHECKSUM_UNNECESSARY",
> nothing more advanced right now, but right now we'd indicate that if
> the packet had 0x0000 in the checksum field, but should've had 0xffff.
>
> On TX I believe we actually do in HW exactly what your patch just did.
Can you describe the visible effects of this problem ?
Is that because of a conversion we might do later to CHECKSUM_COMPLETE ?
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Johannes Berg @ 2017-01-26 13:49 UTC (permalink / raw)
To: Eric Dumazet; +Cc: netdev, linux-wireless
In-Reply-To: <1485438299.5145.117.camel@edumazet-glaptop3.roam.corp.google.com>
On Thu, 2017-01-26 at 05:44 -0800, Eric Dumazet wrote:
> On Thu, 2017-01-26 at 14:27 +0100, Johannes Berg wrote:
> > Hi,
> >
> > It looks like right now we may have a hardware bug and accept
> > 0x0000 as
> > valid, when the outcome of the calculation is 0xffff.
> >
> > What do you think we should do about this?
> >
> > We could ignore the issue entirely, since 0 wasn't ever supposed to
> > be
> > sent anyway - but then we don't drop frames that we should drop. I
> > didn't manage to find the code in the IPv6/UDP stack that even does
> > that, but I assume it's there somewhere.
> >
> > Alternatively, we could parse the packet to find the checksum
> > inside,
> > and if it's 0 then don't report CHECKSUM_UNNECESSARY, but that
> > seems
> > rather expensive/difficult due to the IPv6 variable header and all
> > that. If we wanted to go this route, are there any helper functions
> > for
> > this?
> >
> > Unfortunately, in the current devices, we neither have a complete
> > indication that the packet was even UDP-IPv6, nor do we have the
> > raw
> > csum or anything like that. I think they're adding that to the next
> > hardware spin, but we probably need to address this issue now.
> Is this a xmit or rcv problem ?
Oops, sorry - receive. We can only indicate "CHECKSUM_UNNECESSARY",
nothing more advanced right now, but right now we'd indicate that if
the packet had 0x0000 in the checksum field, but should've had 0xffff.
On TX I believe we actually do in HW exactly what your patch just did.
johannes
^ permalink raw reply
* Re: IPv6-UDP 0x0000 checksum
From: Eric Dumazet @ 2017-01-26 13:44 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <1485437276.14760.3.camel@sipsolutions.net>
On Thu, 2017-01-26 at 14:27 +0100, Johannes Berg wrote:
> Hi,
>
> It looks like right now we may have a hardware bug and accept 0x0000 as
> valid, when the outcome of the calculation is 0xffff.
>
> What do you think we should do about this?
>
> We could ignore the issue entirely, since 0 wasn't ever supposed to be
> sent anyway - but then we don't drop frames that we should drop. I
> didn't manage to find the code in the IPv6/UDP stack that even does
> that, but I assume it's there somewhere.
>
> Alternatively, we could parse the packet to find the checksum inside,
> and if it's 0 then don't report CHECKSUM_UNNECESSARY, but that seems
> rather expensive/difficult due to the IPv6 variable header and all
> that. If we wanted to go this route, are there any helper functions for
> this?
>
> Unfortunately, in the current devices, we neither have a complete
> indication that the packet was even UDP-IPv6, nor do we have the raw
> csum or anything like that. I think they're adding that to the next
> hardware spin, but we probably need to address this issue now.
>
> johannes
Hi Johannes
I am afraid information is missing.
Is this a xmit or rcv problem ?
I recently fixed an issue, could this be this ?
commit 4f2e4ad56a65f3b7d64c258e373cb71e8d2499f4
Author: Eric Dumazet <edumazet@google.com>
Date: Sat Oct 29 11:02:36 2016 -0700
net: mangle zero checksum in skb_checksum_help()
Sending zero checksum is ok for TCP, but not for UDP.
UDPv6 receiver should by default drop a frame with a 0 checksum,
and UDPv4 would not verify the checksum and might accept a corrupted
packet.
Simply replace such checksum by 0xffff, regardless of transport.
This error was caught on SIT tunnels, but seems generic.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Maciej Żenczykowski <maze@google.com>
Cc: Willem de Bruijn <willemb@google.com>
Acked-by: Maciej Żenczykowski <maze@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/net/core/dev.c b/net/core/dev.c
index 820bac239738eb021354ac95ca5bbdff1840cb8e..eaad4c28069ff523ac784bf2dffd0acff82341a0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2484,7 +2484,7 @@ int skb_checksum_help(struct sk_buff *skb)
goto out;
}
- *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum) ?: CSUM_MANGLED_0;
out_set_summed:
skb->ip_summed = CHECKSUM_NONE;
out:
^ permalink raw reply related
* IPv6-UDP 0x0000 checksum
From: Johannes Berg @ 2017-01-26 13:27 UTC (permalink / raw)
To: netdev; +Cc: linux-wireless
Hi,
It looks like right now we may have a hardware bug and accept 0x0000 as
valid, when the outcome of the calculation is 0xffff.
What do you think we should do about this?
We could ignore the issue entirely, since 0 wasn't ever supposed to be
sent anyway - but then we don't drop frames that we should drop. I
didn't manage to find the code in the IPv6/UDP stack that even does
that, but I assume it's there somewhere.
Alternatively, we could parse the packet to find the checksum inside,
and if it's 0 then don't report CHECKSUM_UNNECESSARY, but that seems
rather expensive/difficult due to the IPv6 variable header and all
that. If we wanted to go this route, are there any helper functions for
this?
Unfortunately, in the current devices, we neither have a complete
indication that the packet was even UDP-IPv6, nor do we have the raw
csum or anything like that. I think they're adding that to the next
hardware spin, but we probably need to address this issue now.
johannes
^ permalink raw reply
* Re: [PATCH 3/4] ath9k: check for deaf rx path state
From: Simon Wunderlich @ 2017-01-26 10:32 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, kvalo
In-Reply-To: <8306f20d-ca2a-60fd-b0d9-5155f3bbd094@nbd.name>
[-- Attachment #1: Type: text/plain, Size: 2713 bytes --]
Hi Felix,
On Thursday, January 26, 2017 11:26:03 AM CET Felix Fietkau wrote:
> On 2017-01-26 11:15, Simon Wunderlich wrote:
> > Hey,
> >
> > On Thursday, January 26, 2017 11:02:53 AM CET Felix Fietkau wrote:
> >> On 2017-01-26 10:50, Simon Wunderlich wrote:
> >> > Hey Felix,
> >> >
> >> > On Wednesday, January 25, 2017 5:36:53 PM CET Felix Fietkau wrote:
> >> >> Various chips occasionally run into a state where the tx path still
> >> >> appears to be working normally, but the rx path is deaf.
> >> >>
> >> >> There is no known register signature to check for this state
> >> >> explicitly,
> >> >> so use the lack of rx interrupts as an indicator.
> >> >>
> >> >> This detection is prone to false positives, since a device could also
> >> >> simply be in an environment where there are no frames on the air.
> >> >> However, in this case doing a reset should be harmless since it's
> >> >> obviously not interrupting any real activity. To avoid confusion, call
> >> >> the reset counters in this case "Rx path inactive" instead of
> >> >> something
> >> >> like "Rx path deaf", since it may not be an indication of a real
> >> >> hardware failure.
> >> >>
> >> >> Signed-off-by: Felix Fietkau <nbd@nbd.name>
> >> >
> >> > As we observed in the field, it may happen that there are still RX
> >> > interrupts triggered, but just a very low number - in which case I
> >> > believe your version wouldn't fix the problem. Therefore we had a
> >> > threshold in our original patch [1].
> >>
> >> It seems that you were seeing something different than what I was seeing
> >> in my tests. Though it could be that my issues were actually caused by
> >> something else. I had queued up these changes a while back before I
> >> finally found and fixed the IRQ issue.
> >
> > What we found a good threshold was to check for less than 1 RX interrupt
> > per second, and check the mean average (about) every 30 seconds. If there
> > is any other AP or a station connected, it will not reset the chip, and
> > also there will be no reset on short outages.
>
> But if there's less than 1 Rx interrupt per second, then my patch should
> also trigger, right?
yes, that function you hooked in is called once a second. However, this will
likely lead to one reset per second one a "lonely" access point, which could
create problems for clients connecting the first time, or power-saving clients
who don't talk much. It's not so unlikely that an AP will not hear anything
for a full second, and the reset puts it out of operation for some time, too.
(Not sure how much beacons etc are affected, for example) If you can check
only every 30 seconds on the average, you would reduce this problem.
Cheers,
Simon
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH 3/4] ath9k: check for deaf rx path state
From: Felix Fietkau @ 2017-01-26 10:26 UTC (permalink / raw)
To: Simon Wunderlich; +Cc: linux-wireless, kvalo
In-Reply-To: <2448336.mzt5URIzpg@prime>
On 2017-01-26 11:15, Simon Wunderlich wrote:
> Hey,
>
> On Thursday, January 26, 2017 11:02:53 AM CET Felix Fietkau wrote:
>> On 2017-01-26 10:50, Simon Wunderlich wrote:
>> > Hey Felix,
>> >
>> > On Wednesday, January 25, 2017 5:36:53 PM CET Felix Fietkau wrote:
>> >> Various chips occasionally run into a state where the tx path still
>> >> appears to be working normally, but the rx path is deaf.
>> >>
>> >> There is no known register signature to check for this state explicitly,
>> >> so use the lack of rx interrupts as an indicator.
>> >>
>> >> This detection is prone to false positives, since a device could also
>> >> simply be in an environment where there are no frames on the air.
>> >> However, in this case doing a reset should be harmless since it's
>> >> obviously not interrupting any real activity. To avoid confusion, call
>> >> the reset counters in this case "Rx path inactive" instead of something
>> >> like "Rx path deaf", since it may not be an indication of a real
>> >> hardware failure.
>> >>
>> >> Signed-off-by: Felix Fietkau <nbd@nbd.name>
>> >
>> > As we observed in the field, it may happen that there are still RX
>> > interrupts triggered, but just a very low number - in which case I
>> > believe your version wouldn't fix the problem. Therefore we had a
>> > threshold in our original patch [1].
>>
>> It seems that you were seeing something different than what I was seeing
>> in my tests. Though it could be that my issues were actually caused by
>> something else. I had queued up these changes a while back before I
>> finally found and fixed the IRQ issue.
>
> What we found a good threshold was to check for less than 1 RX interrupt per
> second, and check the mean average (about) every 30 seconds. If there is any
> other AP or a station connected, it will not reset the chip, and also there
> will be no reset on short outages.
But if there's less than 1 Rx interrupt per second, then my patch should
also trigger, right?
- Felix
^ permalink raw reply
* Re: [PATCH 3/4] ath9k: check for deaf rx path state
From: Simon Wunderlich @ 2017-01-26 10:15 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, kvalo
In-Reply-To: <809a5011-4361-0459-2937-5dd5b0d619c2@nbd.name>
[-- Attachment #1: Type: text/plain, Size: 2255 bytes --]
Hey,
On Thursday, January 26, 2017 11:02:53 AM CET Felix Fietkau wrote:
> On 2017-01-26 10:50, Simon Wunderlich wrote:
> > Hey Felix,
> >
> > On Wednesday, January 25, 2017 5:36:53 PM CET Felix Fietkau wrote:
> >> Various chips occasionally run into a state where the tx path still
> >> appears to be working normally, but the rx path is deaf.
> >>
> >> There is no known register signature to check for this state explicitly,
> >> so use the lack of rx interrupts as an indicator.
> >>
> >> This detection is prone to false positives, since a device could also
> >> simply be in an environment where there are no frames on the air.
> >> However, in this case doing a reset should be harmless since it's
> >> obviously not interrupting any real activity. To avoid confusion, call
> >> the reset counters in this case "Rx path inactive" instead of something
> >> like "Rx path deaf", since it may not be an indication of a real
> >> hardware failure.
> >>
> >> Signed-off-by: Felix Fietkau <nbd@nbd.name>
> >
> > As we observed in the field, it may happen that there are still RX
> > interrupts triggered, but just a very low number - in which case I
> > believe your version wouldn't fix the problem. Therefore we had a
> > threshold in our original patch [1].
>
> It seems that you were seeing something different than what I was seeing
> in my tests. Though it could be that my issues were actually caused by
> something else. I had queued up these changes a while back before I
> finally found and fixed the IRQ issue.
What we found a good threshold was to check for less than 1 RX interrupt per
second, and check the mean average (about) every 30 seconds. If there is any
other AP or a station connected, it will not reset the chip, and also there
will be no reset on short outages.
However, Access Points "alone" somewhere without users may still accumulate a
lot of false resets. Your name choice is better than ours in this regard, lets
hope users understand that as well. :)
>
> > We would also appreciate if you can at least briefly mention our work if
> > you resend/rework our stuff.
>
> Sorry about that. I rebased this from older experimental patches and
> forgot to put in all the relevant context.
Cool, thanks!
Simon
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH 3/4] ath9k: check for deaf rx path state
From: Felix Fietkau @ 2017-01-26 10:02 UTC (permalink / raw)
To: Simon Wunderlich; +Cc: linux-wireless, kvalo
In-Reply-To: <4839692.lfma8z9lJt@prime>
On 2017-01-26 10:50, Simon Wunderlich wrote:
> Hey Felix,
>
>
> On Wednesday, January 25, 2017 5:36:53 PM CET Felix Fietkau wrote:
>> Various chips occasionally run into a state where the tx path still
>> appears to be working normally, but the rx path is deaf.
>>
>> There is no known register signature to check for this state explicitly,
>> so use the lack of rx interrupts as an indicator.
>>
>> This detection is prone to false positives, since a device could also
>> simply be in an environment where there are no frames on the air.
>> However, in this case doing a reset should be harmless since it's
>> obviously not interrupting any real activity. To avoid confusion, call
>> the reset counters in this case "Rx path inactive" instead of something
>> like "Rx path deaf", since it may not be an indication of a real
>> hardware failure.
>>
>> Signed-off-by: Felix Fietkau <nbd@nbd.name>
>
> As we observed in the field, it may happen that there are still RX interrupts
> triggered, but just a very low number - in which case I believe your version
> wouldn't fix the problem. Therefore we had a threshold in our original patch
> [1].
It seems that you were seeing something different than what I was seeing
in my tests. Though it could be that my issues were actually caused by
something else. I had queued up these changes a while back before I
finally found and fixed the IRQ issue.
> We would also appreciate if you can at least briefly mention our work if you
> resend/rework our stuff.
Sorry about that. I rebased this from older experimental patches and
forgot to put in all the relevant context.
Kalle, please drop this change for now.
- Felix
^ permalink raw reply
* Re: [PATCH 3/4] ath9k: check for deaf rx path state
From: Simon Wunderlich @ 2017-01-26 9:50 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, kvalo
In-Reply-To: <20170125163654.66431-3-nbd@nbd.name>
[-- Attachment #1: Type: text/plain, Size: 1248 bytes --]
Hey Felix,
On Wednesday, January 25, 2017 5:36:53 PM CET Felix Fietkau wrote:
> Various chips occasionally run into a state where the tx path still
> appears to be working normally, but the rx path is deaf.
>
> There is no known register signature to check for this state explicitly,
> so use the lack of rx interrupts as an indicator.
>
> This detection is prone to false positives, since a device could also
> simply be in an environment where there are no frames on the air.
> However, in this case doing a reset should be harmless since it's
> obviously not interrupting any real activity. To avoid confusion, call
> the reset counters in this case "Rx path inactive" instead of something
> like "Rx path deaf", since it may not be an indication of a real
> hardware failure.
>
> Signed-off-by: Felix Fietkau <nbd@nbd.name>
As we observed in the field, it may happen that there are still RX interrupts
triggered, but just a very low number - in which case I believe your version
wouldn't fix the problem. Therefore we had a threshold in our original patch
[1].
We would also appreciate if you can at least briefly mention our work if you
resend/rework our stuff.
Thank you,
Simon
[1] https://patchwork.kernel.org/patch/9433621/
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [RFC 3/3] cfg80211: Share Channel DFS state across wiphys of same DFS domain
From: Johannes Berg @ 2017-01-26 9:41 UTC (permalink / raw)
To: Vasanthakumar Thiagarajan; +Cc: linux-wireless
In-Reply-To: <1485343870-23601-4-git-send-email-vthiagar@qti.qualcomm.com>
On Wed, 2017-01-25 at 17:01 +0530, Vasanthakumar Thiagarajan wrote:
> Sharing DFS channel state across multiple wiphys (radios) could
> be useful with multiple radios on the system. When one radio
> completes CAC and marks the channel available another radio
> can use this information and start beaconing without really doing
> CAC.
>
> Whenever there is a state change in DFS channel associated to
> a particular wiphy the the same state change is propagated to
> other wiphys having the same DFS reg domain configuration.
> Also when a new wiphy is created the DFS channel state of
> other existing wiphys of same DFS domain is copied.
>
> Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
> ---
> net/wireless/chan.c | 24 +++++++++--
> net/wireless/core.c | 37 +++++++++++++++++
> net/wireless/core.h | 6 +++
> net/wireless/mlme.c | 11 +++++-
> net/wireless/reg.c | 112
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
> net/wireless/reg.h | 22 +++++++++++
> 6 files changed, 207 insertions(+), 5 deletions(-)
>
> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
> index 090309a..40f1097 100644
> --- a/net/wireless/chan.c
> +++ b/net/wireless/chan.c
> @@ -532,21 +532,37 @@ bool cfg80211_beaconing_iface_active(struct
> wireless_dev *wdev)
> return active;
> }
>
> +static bool cfg80211_5ghz_is_wiphy_oper_chan(struct wiphy *wiphy,
> + struct
> ieee80211_channel *chan)
again, nothing really 5 GHz specific here, afaict?
> + struct wireless_dev *wdev;
> +
> + list_for_each_entry(wdev, &wiphy->wdev_list, list) {
> + if (!cfg80211_beaconing_iface_active(wdev))
> + continue;
> +
> + if (cfg80211_5ghz_sub_chan(&wdev->chandef, chan))
> + return true;
> + }
> +
> + return false;
> +}
> +
> bool cfg80211_5ghz_any_wiphy_oper_chan(struct wiphy *wiphy,
> struct ieee80211_channel
> *chan)
> {
> - struct wireless_dev *wdev;
> + struct cfg80211_registered_device *rdev;
>
> ASSERT_RTNL();
>
> if (!(chan->flags & IEEE80211_CHAN_RADAR))
> return false;
>
> - list_for_each_entry(wdev, &wiphy->wdev_list, list) {
> - if (!cfg80211_beaconing_iface_active(wdev))
> + list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
> + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
> continue;
>
> - if (cfg80211_5ghz_sub_chan(&wdev->chandef, chan))
> + if (cfg80211_5ghz_is_wiphy_oper_chan(&rdev->wiphy,
> chan))
> return true;
> }
>
> diff --git a/net/wireless/core.c b/net/wireless/core.c
> index 903fc419..c3fe44b 100644
> --- a/net/wireless/core.c
> +++ b/net/wireless/core.c
> @@ -357,6 +357,38 @@ static void cfg80211_sched_scan_stop_wk(struct
> work_struct *work)
> rtnl_unlock();
> }
>
> +static void cfg80211_propagate_radar_detect_wk(struct work_struct
> *work)
> +{
> + struct cfg80211_registered_device *rdev;
> +
> + rdev = container_of(work, struct cfg80211_registered_device,
> + porpagate_radar_detect_wk);
> +
> + rtnl_lock();
> +
> + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev-
> >radar_chandef,
> + NL80211_DFS_UNAVAILABLE,
> + NL80211_RADAR_DETECTED);
> +
> + rtnl_unlock();
> +}
> +
> +static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
> +{
> + struct cfg80211_registered_device *rdev;
> +
> + rdev = container_of(work, struct cfg80211_registered_device,
> + propagate_cac_done_wk);
> +
> + rtnl_lock();
> +
> + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev-
> >cac_done_chandef,
> + NL80211_DFS_AVAILABLE,
> + NL80211_RADAR_CAC_FINISHED);
> +
> + rtnl_unlock();
> +}
> +
> /* exported functions */
>
> struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int
> sizeof_priv,
> @@ -456,6 +488,9 @@ struct wiphy *wiphy_new_nm(const struct
> cfg80211_ops *ops, int sizeof_priv,
> spin_lock_init(&rdev->destroy_list_lock);
> INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
> INIT_WORK(&rdev->sched_scan_stop_wk,
> cfg80211_sched_scan_stop_wk);
> + INIT_WORK(&rdev->porpagate_radar_detect_wk,
> + cfg80211_propagate_radar_detect_wk);
> + INIT_WORK(&rdev->propagate_cac_done_wk,
> cfg80211_propagate_cac_done_wk);
>
> #ifdef CONFIG_CFG80211_DEFAULT_PS
> rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
> @@ -914,6 +949,8 @@ void wiphy_unregister(struct wiphy *wiphy)
> flush_work(&rdev->destroy_work);
> flush_work(&rdev->sched_scan_stop_wk);
> flush_work(&rdev->mlme_unreg_wk);
> + flush_work(&rdev->porpagate_radar_detect_wk);
typo: propagate.
> + flush_work(&rdev->propagate_cac_done_wk);
You got it right here :)
> #ifdef CONFIG_PM
> if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
> diff --git a/net/wireless/core.h b/net/wireless/core.h
> index 327fe95..607c8be 100644
> --- a/net/wireless/core.h
> +++ b/net/wireless/core.h
> @@ -97,6 +97,12 @@ struct cfg80211_registered_device {
>
> struct work_struct sched_scan_stop_wk;
>
> + struct cfg80211_chan_def radar_chandef;
> + struct work_struct porpagate_radar_detect_wk;
Since it compiled, the typo exists everywhere this is referenced.
> +++ b/net/wireless/mlme.c
> @@ -18,7 +18,6 @@
> #include "nl80211.h"
> #include "rdev-ops.h"
>
> -
> void cfg80211_rx_assoc_resp(struct net_device *dev, struct
> cfg80211_bss *bss,
please don't make unrelated "cleanups".
> +bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2)
> +{
> + const struct ieee80211_regdomain *wiphy1_regd = NULL;
> + const struct ieee80211_regdomain *wiphy2_regd = NULL;
> + const struct ieee80211_regdomain *cfg80211_regd = NULL;
> + bool dfs_domain_same = false;
You can remove that initializer,
> + rcu_read_lock();
> +
> + cfg80211_regd = rcu_dereference(cfg80211_regdomain);
> + wiphy1_regd = rcu_dereference(wiphy1->regd);
> + if (!wiphy1_regd)
> + wiphy1_regd = cfg80211_regd;
> +
> + wiphy2_regd = rcu_dereference(wiphy2->regd);
> + if (!wiphy2_regd)
> + wiphy2_regd = cfg80211_regd;
> +
> + if (wiphy1_regd->dfs_region == wiphy2_regd->dfs_region)
> + dfs_domain_same = true;
and just assign
dfs_domain_same = wiphy1... == wiphy2...;
> +static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy,
> + struct wiphy *src_wiphy)
> +{
> + struct ieee80211_supported_band *src_sband, *dst_sband;
> + int i, j;
> +
> + dst_sband = dst_wiphy->bands[NL80211_BAND_5GHZ];
> + src_sband = src_wiphy->bands[NL80211_BAND_5GHZ];
> + if (!dst_sband || !src_sband)
> + return;
Why make this 5 GHz specific? Perhaps some kind of radar stuff will
exist in future bands too. It shouldn't really cost much to iterate all
the bands, I think?
johannes
^ permalink raw reply
* Re: [RFC 2/3] cfg80211: Disallow moving out of operating DFS channel in non-ETSI
From: Johannes Berg @ 2017-01-26 9:36 UTC (permalink / raw)
To: Vasanthakumar Thiagarajan; +Cc: linux-wireless
In-Reply-To: <1485343870-23601-3-git-send-email-vthiagar@qti.qualcomm.com>
> +static bool cfg80211_off_channel_oper_allowed(struct wireless_dev
> *wdev)
> +{
> + if (!cfg80211_beaconing_iface_active(wdev))
> + return true;
> +
> + if (!(wdev->chandef.chan->flags & IEEE80211_CHAN_RADAR))
> + return true;
That could use some locking assertions. Maybe also in the
cfg80211_beaconing_iface_active() function you introduced in the
previous patch.
> + if (!cfg80211_off_channel_oper_allowed(wdev)) {
> + struct ieee80211_channel *chan;
> +
> + if (request->n_channels != 1) {
> + err = -EBUSY;
> + goto out_free;
> + }
> +
> + chan = request->channels[0];
> + if (chan->center_freq != wdev->chandef.chan-
> >center_freq) {
> + err = -EBUSY;
> + goto out_free;
> + }
> + }
I'm not convinced you even hold the relevant locks here, though off the
top of my head I'm not even sure which are needed.
> i = 0;
> if (n_ssids) {
> nla_for_each_nested(attr, info-
> >attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
> @@ -9053,6 +9079,7 @@ static int nl80211_remain_on_channel(struct
> sk_buff *skb,
> struct cfg80211_registered_device *rdev = info->user_ptr[0];
> struct wireless_dev *wdev = info->user_ptr[1];
> struct cfg80211_chan_def chandef;
> + const struct cfg80211_chan_def *compat_chandef;
> struct sk_buff *msg;
> void *hdr;
> u64 cookie;
> @@ -9081,6 +9108,14 @@ static int nl80211_remain_on_channel(struct
> sk_buff *skb,
> if (err)
> return err;
>
> + if (!(cfg80211_off_channel_oper_allowed(wdev) ||
> + cfg80211_chandef_identical(&wdev->chandef, &chandef)))
I'd prefer to write that as !off_channel && !chandef_identical, seems
easier to understand here.
johannes
^ permalink raw reply
* Re: [RFC 1/3] cfg80211: Make pre-CAC results valid only for ETSI domain
From: Johannes Berg @ 2017-01-26 9:34 UTC (permalink / raw)
To: Vasanthakumar Thiagarajan; +Cc: linux-wireless
In-Reply-To: <1485343870-23601-2-git-send-email-vthiagar@qti.qualcomm.com>
> + /* Should we apply the grace period during beaconing
> interface
> + * shutdown also?
> + */
> + cfg80211_sched_dfs_chan_update(rdev);
It might make some sense, say if hostapd crashes and you restart it
automatically or something?
> return err;
> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
> index 5497d022..090309a 100644
> --- a/net/wireless/chan.c
> +++ b/net/wireless/chan.c
> @@ -456,6 +456,102 @@ bool cfg80211_chandef_dfs_usable(struct wiphy
> *wiphy,
> return (r1 + r2 > 0);
> }
>
> +static bool cfg80211_5ghz_sub_chan(struct cfg80211_chan_def
> *chandef,
> + struct ieee80211_channel *chan)
This could use some explanation, and I don't see anything that's really
5 GHz specific in here, so why that in the function name?
> + u32 start_freq_seg0 = 0, end_freq_seg0 = 0;
> + u32 start_freq_seg1 = 0, end_freq_seg1 = 0;
> +
> + if (chandef->chan->center_freq == chan->center_freq)
> + return true;
> +
> + switch (chandef->width) {
> + case NL80211_CHAN_WIDTH_40:
> + start_freq_seg0 = chandef->center_freq1 - 20;
> + end_freq_seg0 = chandef->center_freq1 + 20;
> + break;
> + case NL80211_CHAN_WIDTH_80P80:
> + start_freq_seg1 = chandef->center_freq2 - 40;
> + end_freq_seg1 = chandef->center_freq2 + 40;
> + /* fall through */
> + case NL80211_CHAN_WIDTH_80:
> + start_freq_seg0 = chandef->center_freq1 - 40;
> + end_freq_seg0 = chandef->center_freq1 + 40;
> + break;
> + case NL80211_CHAN_WIDTH_160:
> + start_freq_seg0 = chandef->center_freq1 - 80;
> + end_freq_seg0 = chandef->center_freq1 + 80;
> + break;
> + case NL80211_CHAN_WIDTH_20_NOHT:
> + case NL80211_CHAN_WIDTH_20:
> + case NL80211_CHAN_WIDTH_5:
> + case NL80211_CHAN_WIDTH_10:
> + break;
> + }
> +
> + if (chan->center_freq > start_freq_seg0 &&
> + chan->center_freq < end_freq_seg0)
> + return true;
> +
> + return chan->center_freq > start_freq_seg1 &&
> + chan->center_freq < end_freq_seg1;
> +}
It's also written pretty oddly... The 5/10/20 cases could return
immediately, the start/end could be replaced by width, and the
initializations wouldn't be needed at all ... I think we can do better
here.
> +bool cfg80211_5ghz_any_wiphy_oper_chan(struct wiphy *wiphy,
> + struct ieee80211_channel
> *chan)
Again, nothing 5 GHz specific.
> + struct wireless_dev *wdev;
> +
> + ASSERT_RTNL();
> +
> + if (!(chan->flags & IEEE80211_CHAN_RADAR))
> + return false;
> +
> + list_for_each_entry(wdev, &wiphy->wdev_list, list) {
> + if (!cfg80211_beaconing_iface_active(wdev))
> + continue;
> +
> + if (cfg80211_5ghz_sub_chan(&wdev->chandef, chan))
> + return true;
> + }
> +
> + return false;
> +}
>
> static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
> u32 center_freq,
> diff --git a/net/wireless/core.h b/net/wireless/core.h
> index 58ca206..327fe95 100644
> --- a/net/wireless/core.h
> +++ b/net/wireless/core.h
> @@ -459,6 +459,13 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
> cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
> const struct cfg80211_chan_def
> *chandef);
>
> +void cfg80211_sched_dfs_chan_update(struct
> cfg80211_registered_device *rdev);
> +
> +bool cfg80211_5ghz_any_wiphy_oper_chan(struct wiphy *wiphy,
> + struct ieee80211_channel
> *chan);
> +
> +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev);
> +
> static inline unsigned int elapsed_jiffies_msecs(unsigned long
> start)
> {
> unsigned long end = jiffies;
> diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
> index 364f900..10bf040 100644
> --- a/net/wireless/ibss.c
> +++ b/net/wireless/ibss.c
> @@ -190,6 +190,7 @@ static void __cfg80211_clear_ibss(struct
> net_device *dev, bool nowext)
> if (!nowext)
> wdev->wext.ibss.ssid_len = 0;
> #endif
> + cfg80211_sched_dfs_chan_update(rdev);
> }
>
> void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
> diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
> index 2d8518a..ec0b1c2 100644
> --- a/net/wireless/mesh.c
> +++ b/net/wireless/mesh.c
> @@ -262,6 +262,7 @@ int __cfg80211_leave_mesh(struct
> cfg80211_registered_device *rdev,
> wdev->beacon_interval = 0;
> memset(&wdev->chandef, 0, sizeof(wdev->chandef));
> rdev_set_qos_map(rdev, dev, NULL);
> + cfg80211_sched_dfs_chan_update(rdev);
> }
>
> return err;
> diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
> index 22b3d99..3c7e155 100644
> --- a/net/wireless/mlme.c
> +++ b/net/wireless/mlme.c
> @@ -745,6 +745,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev,
> int freq, int sig_mbm,
> }
> EXPORT_SYMBOL(cfg80211_rx_mgmt);
>
> +void cfg80211_sched_dfs_chan_update(struct
> cfg80211_registered_device *rdev)
> +{
> + cancel_delayed_work(&rdev->dfs_update_channels_wk);
> + queue_delayed_work(cfg80211_wq, &rdev-
> >dfs_update_channels_wk, 0);
> +}
This uses 0.
> @@ -820,9 +844,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
> */
> cfg80211_set_dfs_state(wiphy, chandef,
> NL80211_DFS_UNAVAILABLE);
>
> - timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
> - queue_delayed_work(cfg80211_wq, &rdev-
> >dfs_update_channels_wk,
> - timeout);
> + cfg80211_sched_dfs_chan_update(rdev);
But this didn't - why does that change?
> +unsigned long regulatory_get_pre_cac_timeout(struct wiphy *wiphy)
> +{
> + if (!regulatory_pre_cac_allowed(wiphy))
> + return REG_PRE_CAC_EXPIRY_GRACE_MS;
> +
> + /*
> + * Return the maximum pre-CAC timeout when pre-CAC is
> allowed
> + * in the current dfs domain (ETSI).
> + */
> + return -1;
> +}
Don't ever return -1, that's -EPERM and not really what you want
anyway.
In fact, this doesn't even make sense, since the only caller already
checks regulatory_pre_cac_allowed() before calling this.
johannes
^ permalink raw reply
* Re: [PATCH 1/2] mac80211: use helper function to access ieee802_1d_to_ac[]
From: Johannes Berg @ 2017-01-26 8:51 UTC (permalink / raw)
To: Amadeusz Sławiński, linux-wireless
Cc: David S. Miller, netdev, linux-kernel
In-Reply-To: <1485272531-11587-1-git-send-email-amadeusz.slawinski@tieto.com>
On Tue, 2017-01-24 at 16:42 +0100, Amadeusz Sławiński wrote:
> cleanup patch to make use of ieee80211_ac_from_tid() to retrieve ac
> from
> ieee802_1d_to_ac[]
Applied.
johannes
^ permalink raw reply
* [PATCH v2] iwlwifi: allow memory debug TLV to specify the memory type
From: Luca Coelho @ 2017-01-26 7:43 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, luciano.coelho, Johannes Berg
In-Reply-To: <20170123220350.25305-6-luca@coelho.fi>
From: Johannes Berg <johannes.berg@intel.com>
Due to some new features and changes, the firmware file will now
specify what type of memory to dump, in upper 8 bits of the type
field of the TLV. Parse it (types we don't understand are errors)
and teach the code to dump periphery memory.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
---
In v2:
The prph read should read dwords, but now the offset is incremented
by a byte in each iteration, instead of a dword.
As a result, the prph dump both prints each value multiple times
and not all of the block is read.
Fix this by advancing the read offset in a dword after every
iteration.
drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 12 +++++
drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 15 +++++-
drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 65 +++++++++++++++++++-----
3 files changed, 79 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 431581229948..a6719d67ac00 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1020,6 +1020,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
IWL_DEBUG_INFO(drv, "Found debug memory segment: %u\n",
dbg_mem->data_type);
+ switch (type & FW_DBG_MEM_TYPE_MASK) {
+ case FW_DBG_MEM_TYPE_REGULAR:
+ case FW_DBG_MEM_TYPE_PRPH:
+ /* we know how to handle these */
+ break;
+ default:
+ IWL_ERR(drv,
+ "Found debug memory segment with invalid type: 0x%x\n",
+ type);
+ return -EINVAL;
+ }
+
size = sizeof(*pieces->dbg_mem_tlv) *
(pieces->n_dbg_mem_tlv + 1);
n = krealloc(pieces->dbg_mem_tlv, size, GFP_KERNEL);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
index 11245b9117c7..c84207576587 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
@@ -489,9 +489,22 @@ enum iwl_fw_dbg_monitor_mode {
};
/**
+ * enum iwl_fw_mem_seg_type - memory segment type
+ * @FW_DBG_MEM_TYPE_MASK: mask for the type indication
+ * @FW_DBG_MEM_TYPE_REGULAR: regular memory
+ * @FW_DBG_MEM_TYPE_PRPH: periphery memory (requires special reading)
+ */
+enum iwl_fw_mem_seg_type {
+ FW_DBG_MEM_TYPE_MASK = 0xff000000,
+ FW_DBG_MEM_TYPE_REGULAR = 0x00000000,
+ FW_DBG_MEM_TYPE_PRPH = 0x01000000,
+};
+
+/**
* struct iwl_fw_dbg_mem_seg_tlv - configures the debug data memory segments
*
- * @data_type: the memory segment type to record
+ * @data_type: the memory segment type to record, see &enum iwl_fw_mem_seg_type
+ * for what we care about
* @ofs: the memory segment offset
* @len: the memory segment length, in bytes
*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
index 7df3c3f4749e..e447cd92deb3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
@@ -406,6 +406,30 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
{ .start = 0x00a02400, .end = 0x00a02758 },
};
+static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start,
+ u32 len_bytes, __le32 *data)
+{
+ u32 i;
+
+ for (i = 0; i < len_bytes; i += 4)
+ *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i));
+}
+
+static bool iwl_read_prph_block(struct iwl_trans *trans, u32 start,
+ u32 len_bytes, __le32 *data)
+{
+ unsigned long flags;
+ bool success = false;
+
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ success = true;
+ _iwl_read_prph_block(trans, start, len_bytes, data);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+
+ return success;
+}
+
static void iwl_dump_prph(struct iwl_trans *trans,
struct iwl_fw_error_dump_data **data,
const struct iwl_prph_range *iwl_prph_dump_addr,
@@ -422,21 +446,18 @@ static void iwl_dump_prph(struct iwl_trans *trans,
/* The range includes both boundaries */
int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
iwl_prph_dump_addr[i].start + 4;
- int reg;
- __le32 *val;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
(*data)->len = cpu_to_le32(sizeof(*prph) +
num_bytes_in_chunk);
prph = (void *)(*data)->data;
prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
- val = (void *)prph->data;
- for (reg = iwl_prph_dump_addr[i].start;
- reg <= iwl_prph_dump_addr[i].end;
- reg += 4)
- *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
- reg));
+ _iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start,
+ /* our range is inclusive, hence + 4 */
+ iwl_prph_dump_addr[i].end -
+ iwl_prph_dump_addr[i].start + 4,
+ (void *)prph->data);
*data = iwl_fw_error_next_data(*data);
}
@@ -716,16 +737,36 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
for (i = 0; i < mvm->fw->n_dbg_mem_tlv; i++) {
u32 len = le32_to_cpu(fw_dbg_mem[i].len);
u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs);
+ bool success;
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
dump_data->len = cpu_to_le32(len + sizeof(*dump_mem));
dump_mem = (void *)dump_data->data;
dump_mem->type = fw_dbg_mem[i].data_type;
dump_mem->offset = cpu_to_le32(ofs);
- iwl_trans_read_mem_bytes(mvm->trans, ofs,
- dump_mem->data,
- len);
- dump_data = iwl_fw_error_next_data(dump_data);
+
+ switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) {
+ case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR):
+ iwl_trans_read_mem_bytes(mvm->trans, ofs,
+ dump_mem->data,
+ len);
+ success = true;
+ break;
+ case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH):
+ success = iwl_read_prph_block(mvm->trans, ofs, len,
+ (void *)dump_mem->data);
+ break;
+ default:
+ /*
+ * shouldn't get here, we ignored this kind
+ * of TLV earlier during the TLV parsing?!
+ */
+ WARN_ON(1);
+ success = false;
+ }
+
+ if (success)
+ dump_data = iwl_fw_error_next_data(dump_data);
}
if (smem_len) {
--
2.11.0
^ permalink raw reply related
* [PATCH v2] iwlwifi: mvm: use mvm_disable_queue instead of sharing logic
From: Luca Coelho @ 2017-01-26 7:34 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, luciano.coelho, Sara Sharon
In-Reply-To: <20170123220350.25305-25-luca@coelho.fi>
From: Sara Sharon <sara.sharon@intel.com>
When removing inactive queue - use the central disable queue
function. This is needed due to a000 changes to come, but is
a proper cleanup anyway.
Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
---
In v2:
iwl_mvm_disable_txq() returns early if the hw_ref_count is
zeroed, with the result of the queue not being properly disabled.
Avoid zeroing it and other queue information in remove_sta_marking().
iwl_mvm_disable_txq() which is called later in the flow will zero
it.
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++--
drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 29 +++++++-------------------
drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 10 +++++----
3 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 4a9cb76b7611..a672aa71c656 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1657,8 +1657,8 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
* Disable a TXQ.
* Note that in non-DQA mode the %mac80211_queue and %tid params are ignored.
*/
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u8 tid, u8 flags);
+int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u8 tid, u8 flags);
int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq);
/* Return a bitmask with all the hw supported queues, except for the
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index cf8222a8b588..40e7fc5f1f1f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -454,13 +454,6 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue)
rcu_read_unlock();
- spin_lock_bh(&mvm->queue_info_lock);
- /* Unmap MAC queues and TIDs from this queue */
- mvm->queue_info[queue].hw_queue_to_mac80211 = 0;
- mvm->queue_info[queue].hw_queue_refcount = 0;
- mvm->queue_info[queue].tid_bitmap = 0;
- spin_unlock_bh(&mvm->queue_info_lock);
-
return disable_agg_tids;
}
@@ -755,28 +748,22 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
* first
*/
if (using_inactive_queue) {
- struct iwl_scd_txq_cfg_cmd cmd = {
- .scd_queue = queue,
- .action = SCD_CFG_DISABLE_QUEUE,
- };
- u8 txq_curr_ac;
-
- disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue);
+ u8 txq_curr_ac, sta_id;
spin_lock_bh(&mvm->queue_info_lock);
txq_curr_ac = mvm->queue_info[queue].mac80211_ac;
- cmd.sta_id = mvm->queue_info[queue].ra_sta_id;
- cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[txq_curr_ac];
- cmd.tid = mvm->queue_info[queue].txq_tid;
+ sta_id = mvm->queue_info[queue].ra_sta_id;
spin_unlock_bh(&mvm->queue_info_lock);
+ disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue);
/* Disable the queue */
if (disable_agg_tids)
iwl_mvm_invalidate_sta_queue(mvm, queue,
disable_agg_tids, false);
- iwl_trans_txq_disable(mvm->trans, queue, false);
- ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd),
- &cmd);
+
+ ret = iwl_mvm_disable_txq(mvm, queue,
+ mvmsta->vif->hw_queue[txq_curr_ac],
+ tid, 0);
if (ret) {
IWL_ERR(mvm,
"Failed to free inactive queue %d (ret=%d)\n",
@@ -791,7 +778,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm,
}
/* If TXQ is allocated to another STA, update removal in FW */
- if (cmd.sta_id != mvmsta->sta_id)
+ if (sta_id != mvmsta->sta_id)
iwl_mvm_invalidate_sta_queue(mvm, queue, 0, true);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index d04babd99b53..2beea3b98e52 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -706,8 +706,8 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
}
}
-void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
- u8 tid, u8 flags)
+int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
+ u8 tid, u8 flags)
{
struct iwl_scd_txq_cfg_cmd cmd = {
.scd_queue = queue,
@@ -720,7 +720,7 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) {
spin_unlock_bh(&mvm->queue_info_lock);
- return;
+ return 0;
}
mvm->queue_info[queue].tid_bitmap &= ~BIT(tid);
@@ -760,7 +760,7 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
/* If the queue is still enabled - nothing left to do in this func */
if (cmd.action == SCD_CFG_ENABLE_QUEUE) {
spin_unlock_bh(&mvm->queue_info_lock);
- return;
+ return 0;
}
cmd.sta_id = mvm->queue_info[queue].ra_sta_id;
@@ -791,6 +791,8 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
if (ret)
IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n",
queue, ret);
+
+ return ret;
}
/**
--
2.11.0
^ permalink raw reply related
* [PATCH v2] iwlwifi: mvm: change iwl_mvm_tx_csum to return value
From: Luca Coelho @ 2017-01-26 7:24 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, luciano.coelho, Sara Sharon
In-Reply-To: <20170123220350.25305-12-luca@coelho.fi>
From: Sara Sharon <sara.sharon@intel.com>
Currently the function changes the TX cmd itself.
Make it more generic by returning a value, as preperation
to the new TX cmd.
Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
---
In v2:
* Fix compilation warning when CONFIG_INET is not set
drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 66957ac12ca4..73a90d4a43eb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -102,14 +102,13 @@ iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
#define OPT_HDR(type, skb, off) \
(type *)(skb_network_header(skb) + (off))
-static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_hdr *hdr,
- struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd)
+static u16 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *info)
{
+ u16 offload_assist = 0;
#if IS_ENABLED(CONFIG_INET)
u16 mh_len = ieee80211_hdrlen(hdr->frame_control);
- u16 offload_assist = le16_to_cpu(tx_cmd->offload_assist);
u8 protocol = 0;
/*
@@ -117,7 +116,7 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
* compute it
*/
if (skb->ip_summed != CHECKSUM_PARTIAL || IWL_MVM_SW_TX_CSUM_OFFLOAD)
- return;
+ goto out;
/* We do not expect to be requested to csum stuff we do not support */
if (WARN_ONCE(!(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) ||
@@ -125,7 +124,7 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
skb->protocol != htons(ETH_P_IPV6)),
"No support for requested checksum\n")) {
skb_checksum_help(skb);
- return;
+ goto out;
}
if (skb->protocol == htons(ETH_P_IP)) {
@@ -145,7 +144,7 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
protocol != NEXTHDR_HOP &&
protocol != NEXTHDR_DEST) {
skb_checksum_help(skb);
- return;
+ goto out;
}
hp = OPT_HDR(struct ipv6_opt_hdr, skb, off);
@@ -159,7 +158,7 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) {
WARN_ON_ONCE(1);
skb_checksum_help(skb);
- return;
+ goto out;
}
/* enable L4 csum */
@@ -191,8 +190,9 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb,
mh_len /= 2;
offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE;
- tx_cmd->offload_assist = cpu_to_le16(offload_assist);
+out:
#endif
+ return offload_assist;
}
/*
@@ -295,7 +295,8 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
!(tx_cmd->offload_assist & cpu_to_le16(BIT(TX_CMD_OFFLD_AMSDU))))
tx_cmd->offload_assist |= cpu_to_le16(BIT(TX_CMD_OFFLD_PAD));
- iwl_mvm_tx_csum(mvm, skb, hdr, info, tx_cmd);
+ tx_cmd->offload_assist |=
+ cpu_to_le16(iwl_mvm_tx_csum(mvm, skb, hdr, info));
}
/*
--
2.11.0
^ permalink raw reply related
* Re: [PATCH] NFC: remove TI nfcwilink driver
From: Marcel Holtmann @ 2017-01-26 5:54 UTC (permalink / raw)
To: Rob Herring
Cc: linux-kernel, linux-wireless, Ilan Elias, Samuel Ortiz,
Lauro Ramos Venancio, Aloisio Almeida Jr
In-Reply-To: <20170125222307.3044-1-robh@kernel.org>
Hi Rob,
> It appears that TI WiLink devices including NFC (WL185x/WL189x) never
> shipped. The only information I found were announcements in Feb
> 2012 about the parts. There's been no activity on this driver besided
> common changes since initially added in Jan 2012. There's also no in
> users that instantiate the platform device (nor DT bindings).
>
> This is a first step in removing TI ST (shared transport) driver in
> favor of extending the BT hci_ll driver to support WL183x chips.
since the firmware files TINfcInit_* also never made it into the linux-firmware tree, I have no idea who is using this driver. I am actually fine with removing it since it would be easy enough to bring back based on hci_ll driver once there is hardware to test this on.
Regards
Marcel
^ permalink raw reply
* [PATCH] nl80211: Fix mesh HT operation check
From: Masashi Honma @ 2017-01-25 23:56 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless, Masashi Honma
commit 9757235f451c27deaa88925399f070ff6fcea832 ('nl80211: correct
checks for NL80211_MESHCONF_HT_OPMODE value') missed to mask a flag
when replacing FILL_IN_MESH_PARAM_IF_SET with checking codes. This
could drop the received HT operation value when called by
nl80211_update_mesh_config().
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
net/wireless/nl80211.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e6ed8dc..006c147 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -5924,6 +5924,7 @@ do { \
break;
}
cfg->ht_opmode = ht_opmode;
+ mask |= (1 << (NL80211_MESHCONF_HT_OPMODE - 1));
}
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
1, 65535, mask,
--
2.7.4
^ permalink raw reply related
* [PATCH] NFC: remove TI nfcwilink driver
From: Rob Herring @ 2017-01-25 22:23 UTC (permalink / raw)
To: linux-kernel, linux-wireless
Cc: Ilan Elias, Marcel Holtmann, Samuel Ortiz, Lauro Ramos Venancio,
Aloisio Almeida Jr
It appears that TI WiLink devices including NFC (WL185x/WL189x) never
shipped. The only information I found were announcements in Feb
2012 about the parts. There's been no activity on this driver besided
common changes since initially added in Jan 2012. There's also no in
users that instantiate the platform device (nor DT bindings).
This is a first step in removing TI ST (shared transport) driver in
favor of extending the BT hci_ll driver to support WL183x chips.
Cc: Ilan Elias <ilane@ti.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Lauro Ramos Venancio <lauro.venancio@openbossa.org>
Cc: Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
Cc: linux-wireless@vger.kernel.org
Signed-off-by: Rob Herring <robh@kernel.org>
---
drivers/nfc/Kconfig | 11 -
drivers/nfc/Makefile | 1 -
drivers/nfc/nfcwilink.c | 578 ------------------------------------------------
3 files changed, 590 deletions(-)
delete mode 100644 drivers/nfc/nfcwilink.c
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 9d2369269abf..c4208487fadc 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -5,17 +5,6 @@
menu "Near Field Communication (NFC) devices"
depends on NFC
-config NFC_WILINK
- tristate "Texas Instruments NFC WiLink driver"
- depends on TI_ST && NFC_NCI
- help
- This enables the NFC driver for Texas Instrument's BT/FM/GPS/NFC
- combo devices. This makes use of shared transport line discipline
- core driver to communicate with the NFC core of the combo chip.
-
- Say Y here to compile support for Texas Instrument's NFC WiLink driver
- into the kernel or say M to compile it as module.
-
config NFC_TRF7970A
tristate "Texas Instruments TRF7970a NFC driver"
depends on SPI && NFC_DIGITAL
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index bab8ef06ae35..640b7274371c 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -6,7 +6,6 @@ obj-$(CONFIG_NFC_FDP) += fdp/
obj-$(CONFIG_NFC_PN544) += pn544/
obj-$(CONFIG_NFC_MICROREAD) += microread/
obj-$(CONFIG_NFC_PN533) += pn533/
-obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o
obj-$(CONFIG_NFC_SIM) += nfcsim.o
obj-$(CONFIG_NFC_PORT100) += port100.o
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
deleted file mode 100644
index 3fbd18b8e473..000000000000
--- a/drivers/nfc/nfcwilink.c
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
- * Texas Instrument's NFC Driver For Shared Transport.
- *
- * NFC Driver acts as interface between NCI core and
- * TI Shared Transport Layer.
- *
- * Copyright (C) 2011 Texas Instruments, Inc.
- *
- * Written by Ilan Elias <ilane@ti.com>
- *
- * Acknowledgements:
- * This file is based on btwilink.c, which was written
- * by Raja Mani and Pavan Savoy.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/firmware.h>
-#include <linux/nfc.h>
-#include <net/nfc/nci.h>
-#include <net/nfc/nci_core.h>
-#include <linux/ti_wilink_st.h>
-
-#define NFCWILINK_CHNL 12
-#define NFCWILINK_OPCODE 7
-#define NFCWILINK_MAX_FRAME_SIZE 300
-#define NFCWILINK_HDR_LEN 4
-#define NFCWILINK_OFFSET_LEN_IN_HDR 1
-#define NFCWILINK_LEN_SIZE 2
-#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */
-#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */
-
-#define BTS_FILE_NAME_MAX_SIZE 40
-#define BTS_FILE_HDR_MAGIC 0x42535442
-#define BTS_FILE_CMD_MAX_LEN 0xff
-#define BTS_FILE_ACTION_TYPE_SEND_CMD 1
-
-#define NCI_VS_NFCC_INFO_CMD_GID 0x2f
-#define NCI_VS_NFCC_INFO_CMD_OID 0x12
-#define NCI_VS_NFCC_INFO_RSP_GID 0x4f
-#define NCI_VS_NFCC_INFO_RSP_OID 0x12
-
-struct nfcwilink_hdr {
- __u8 chnl;
- __u8 opcode;
- __le16 len;
-} __packed;
-
-struct nci_vs_nfcc_info_cmd {
- __u8 gid;
- __u8 oid;
- __u8 plen;
-} __packed;
-
-struct nci_vs_nfcc_info_rsp {
- __u8 gid;
- __u8 oid;
- __u8 plen;
- __u8 status;
- __u8 hw_id;
- __u8 sw_ver_x;
- __u8 sw_ver_z;
- __u8 patch_id;
-} __packed;
-
-struct bts_file_hdr {
- __le32 magic;
- __le32 ver;
- __u8 rfu[24];
- __u8 actions[0];
-} __packed;
-
-struct bts_file_action {
- __le16 type;
- __le16 len;
- __u8 data[0];
-} __packed;
-
-struct nfcwilink {
- struct platform_device *pdev;
- struct nci_dev *ndev;
- unsigned long flags;
-
- int st_register_cb_status;
- long (*st_write) (struct sk_buff *);
-
- struct completion completed;
-
- struct nci_vs_nfcc_info_rsp nfcc_info;
-};
-
-/* NFCWILINK driver flags */
-enum {
- NFCWILINK_RUNNING,
- NFCWILINK_FW_DOWNLOAD,
-};
-
-static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
-
-static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(len + NFCWILINK_HDR_LEN, how);
- if (skb)
- skb_reserve(skb, NFCWILINK_HDR_LEN);
-
- return skb;
-}
-
-static void nfcwilink_fw_download_receive(struct nfcwilink *drv,
- struct sk_buff *skb)
-{
- struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data;
-
- /* Detect NCI_VS_NFCC_INFO_RSP and store the result */
- if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) &&
- (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) {
- memcpy(&drv->nfcc_info, rsp,
- sizeof(struct nci_vs_nfcc_info_rsp));
- }
-
- kfree_skb(skb);
-
- complete(&drv->completed);
-}
-
-static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
-{
- struct nci_vs_nfcc_info_cmd *cmd;
- struct sk_buff *skb;
- unsigned long comp_ret;
- int rc;
-
- skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
- GFP_KERNEL);
- if (!skb) {
- nfc_err(&drv->pdev->dev,
- "no memory for nci_vs_nfcc_info_cmd\n");
- return -ENOMEM;
- }
-
- cmd = (struct nci_vs_nfcc_info_cmd *)
- skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
- cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
- cmd->oid = NCI_VS_NFCC_INFO_CMD_OID;
- cmd->plen = 0;
-
- drv->nfcc_info.plen = 0;
-
- rc = nfcwilink_send(drv->ndev, skb);
- if (rc)
- return rc;
-
- comp_ret = wait_for_completion_timeout(&drv->completed,
- msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
- dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
- comp_ret);
- if (comp_ret == 0) {
- nfc_err(&drv->pdev->dev,
- "timeout on wait_for_completion_timeout\n");
- return -ETIMEDOUT;
- }
-
- dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n",
- drv->nfcc_info.plen, drv->nfcc_info.status);
-
- if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
- nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n");
- return -EINVAL;
- }
-
- snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
- "TINfcInit_%d.%d.%d.%d.bts",
- drv->nfcc_info.hw_id,
- drv->nfcc_info.sw_ver_x,
- drv->nfcc_info.sw_ver_z,
- drv->nfcc_info.patch_id);
-
- nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name);
-
- return 0;
-}
-
-static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
-{
- struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data;
- struct sk_buff *skb;
- unsigned long comp_ret;
- int rc;
-
- /* verify valid cmd for the NFC channel */
- if ((len <= sizeof(struct nfcwilink_hdr)) ||
- (len > BTS_FILE_CMD_MAX_LEN) ||
- (hdr->chnl != NFCWILINK_CHNL) ||
- (hdr->opcode != NFCWILINK_OPCODE)) {
- nfc_err(&drv->pdev->dev,
- "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n",
- len, hdr->chnl, hdr->opcode);
- return 0;
- }
-
- /* remove the ST header */
- len -= sizeof(struct nfcwilink_hdr);
- data += sizeof(struct nfcwilink_hdr);
-
- skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
- if (!skb) {
- nfc_err(&drv->pdev->dev, "no memory for bts cmd\n");
- return -ENOMEM;
- }
-
- memcpy(skb_put(skb, len), data, len);
-
- rc = nfcwilink_send(drv->ndev, skb);
- if (rc)
- return rc;
-
- comp_ret = wait_for_completion_timeout(&drv->completed,
- msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
- dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n",
- comp_ret);
- if (comp_ret == 0) {
- nfc_err(&drv->pdev->dev,
- "timeout on wait_for_completion_timeout\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int nfcwilink_download_fw(struct nfcwilink *drv)
-{
- unsigned char file_name[BTS_FILE_NAME_MAX_SIZE];
- const struct firmware *fw;
- __u16 action_type, action_len;
- __u8 *ptr;
- int len, rc;
-
- set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
-
- rc = nfcwilink_get_bts_file_name(drv, file_name);
- if (rc)
- goto exit;
-
- rc = request_firmware(&fw, file_name, &drv->pdev->dev);
- if (rc) {
- nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc);
-
- /* if the file is not found, don't exit with failure */
- if (rc == -ENOENT)
- rc = 0;
-
- goto exit;
- }
-
- len = fw->size;
- ptr = (__u8 *)fw->data;
-
- if ((len == 0) || (ptr == NULL)) {
- dev_dbg(&drv->pdev->dev,
- "request_firmware returned size %d\n", len);
- goto release_fw;
- }
-
- if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
- BTS_FILE_HDR_MAGIC) {
- nfc_err(&drv->pdev->dev, "wrong bts magic number\n");
- rc = -EINVAL;
- goto release_fw;
- }
-
- /* remove the BTS header */
- len -= sizeof(struct bts_file_hdr);
- ptr += sizeof(struct bts_file_hdr);
-
- while (len > 0) {
- action_type =
- __le16_to_cpu(((struct bts_file_action *)ptr)->type);
- action_len =
- __le16_to_cpu(((struct bts_file_action *)ptr)->len);
-
- dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n",
- action_type, action_len);
-
- switch (action_type) {
- case BTS_FILE_ACTION_TYPE_SEND_CMD:
- rc = nfcwilink_send_bts_cmd(drv,
- ((struct bts_file_action *)ptr)->data,
- action_len);
- if (rc)
- goto release_fw;
- break;
- }
-
- /* advance to the next action */
- len -= (sizeof(struct bts_file_action) + action_len);
- ptr += (sizeof(struct bts_file_action) + action_len);
- }
-
-release_fw:
- release_firmware(fw);
-
-exit:
- clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
- return rc;
-}
-
-/* Called by ST when registration is complete */
-static void nfcwilink_register_complete(void *priv_data, int data)
-{
- struct nfcwilink *drv = priv_data;
-
- /* store ST registration status */
- drv->st_register_cb_status = data;
-
- /* complete the wait in nfc_st_open() */
- complete(&drv->completed);
-}
-
-/* Called by ST when receive data is available */
-static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
-{
- struct nfcwilink *drv = priv_data;
- int rc;
-
- if (!skb)
- return -EFAULT;
-
- if (!drv) {
- kfree_skb(skb);
- return -EFAULT;
- }
-
- dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len);
-
- /* strip the ST header
- (apart for the chnl byte, which is not received in the hdr) */
- skb_pull(skb, (NFCWILINK_HDR_LEN-1));
-
- if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) {
- nfcwilink_fw_download_receive(drv, skb);
- return 0;
- }
-
- /* Forward skb to NCI core layer */
- rc = nci_recv_frame(drv->ndev, skb);
- if (rc < 0) {
- nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-/* protocol structure registered with ST */
-static struct st_proto_s nfcwilink_proto = {
- .chnl_id = NFCWILINK_CHNL,
- .max_frame_size = NFCWILINK_MAX_FRAME_SIZE,
- .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */
- .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR,
- .len_size = NFCWILINK_LEN_SIZE,
- .reserve = 0,
- .recv = nfcwilink_receive,
- .reg_complete_cb = nfcwilink_register_complete,
- .write = NULL,
-};
-
-static int nfcwilink_open(struct nci_dev *ndev)
-{
- struct nfcwilink *drv = nci_get_drvdata(ndev);
- unsigned long comp_ret;
- int rc;
-
- if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) {
- rc = -EBUSY;
- goto exit;
- }
-
- nfcwilink_proto.priv_data = drv;
-
- init_completion(&drv->completed);
- drv->st_register_cb_status = -EINPROGRESS;
-
- rc = st_register(&nfcwilink_proto);
- if (rc < 0) {
- if (rc == -EINPROGRESS) {
- comp_ret = wait_for_completion_timeout(
- &drv->completed,
- msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
-
- dev_dbg(&drv->pdev->dev,
- "wait_for_completion_timeout returned %ld\n",
- comp_ret);
-
- if (comp_ret == 0) {
- /* timeout */
- rc = -ETIMEDOUT;
- goto clear_exit;
- } else if (drv->st_register_cb_status != 0) {
- rc = drv->st_register_cb_status;
- nfc_err(&drv->pdev->dev,
- "st_register_cb failed %d\n", rc);
- goto clear_exit;
- }
- } else {
- nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc);
- goto clear_exit;
- }
- }
-
- /* st_register MUST fill the write callback */
- BUG_ON(nfcwilink_proto.write == NULL);
- drv->st_write = nfcwilink_proto.write;
-
- if (nfcwilink_download_fw(drv)) {
- nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n",
- rc);
- /* open should succeed, even if the FW download failed */
- }
-
- goto exit;
-
-clear_exit:
- clear_bit(NFCWILINK_RUNNING, &drv->flags);
-
-exit:
- return rc;
-}
-
-static int nfcwilink_close(struct nci_dev *ndev)
-{
- struct nfcwilink *drv = nci_get_drvdata(ndev);
- int rc;
-
- if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags))
- return 0;
-
- rc = st_unregister(&nfcwilink_proto);
- if (rc)
- nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc);
-
- drv->st_write = NULL;
-
- return rc;
-}
-
-static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
-{
- struct nfcwilink *drv = nci_get_drvdata(ndev);
- struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
- long len;
-
- dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len);
-
- if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- /* add the ST hdr to the start of the buffer */
- hdr.len = cpu_to_le16(skb->len);
- memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN);
-
- /* Insert skb to shared transport layer's transmit queue.
- * Freeing skb memory is taken care in shared transport layer,
- * so don't free skb memory here.
- */
- len = drv->st_write(skb);
- if (len < 0) {
- kfree_skb(skb);
- nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len);
- return -EFAULT;
- }
-
- return 0;
-}
-
-static struct nci_ops nfcwilink_ops = {
- .open = nfcwilink_open,
- .close = nfcwilink_close,
- .send = nfcwilink_send,
-};
-
-static int nfcwilink_probe(struct platform_device *pdev)
-{
- struct nfcwilink *drv;
- int rc;
- __u32 protocols;
-
- drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL);
- if (!drv) {
- rc = -ENOMEM;
- goto exit;
- }
-
- drv->pdev = pdev;
-
- protocols = NFC_PROTO_JEWEL_MASK
- | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
- | NFC_PROTO_ISO14443_MASK
- | NFC_PROTO_ISO14443_B_MASK
- | NFC_PROTO_NFC_DEP_MASK;
-
- drv->ndev = nci_allocate_device(&nfcwilink_ops,
- protocols,
- NFCWILINK_HDR_LEN,
- 0);
- if (!drv->ndev) {
- nfc_err(&pdev->dev, "nci_allocate_device failed\n");
- rc = -ENOMEM;
- goto exit;
- }
-
- nci_set_parent_dev(drv->ndev, &pdev->dev);
- nci_set_drvdata(drv->ndev, drv);
-
- rc = nci_register_device(drv->ndev);
- if (rc < 0) {
- nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc);
- goto free_dev_exit;
- }
-
- dev_set_drvdata(&pdev->dev, drv);
-
- goto exit;
-
-free_dev_exit:
- nci_free_device(drv->ndev);
-
-exit:
- return rc;
-}
-
-static int nfcwilink_remove(struct platform_device *pdev)
-{
- struct nfcwilink *drv = dev_get_drvdata(&pdev->dev);
- struct nci_dev *ndev;
-
- if (!drv)
- return -EFAULT;
-
- ndev = drv->ndev;
-
- nci_unregister_device(ndev);
- nci_free_device(ndev);
-
- return 0;
-}
-
-static struct platform_driver nfcwilink_driver = {
- .probe = nfcwilink_probe,
- .remove = nfcwilink_remove,
- .driver = {
- .name = "nfcwilink",
- },
-};
-
-module_platform_driver(nfcwilink_driver);
-
-/* ------ Module Info ------ */
-
-MODULE_AUTHOR("Ilan Elias <ilane@ti.com>");
-MODULE_DESCRIPTION("NFC Driver for TI Shared Transport");
-MODULE_LICENSE("GPL");
--
2.10.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox