* Re: FW: [PATCH 2/2] ath10k: allow ATH10K_SNOC with COMPILE_TEST
From: Niklas Cassel @ 2018-06-13 13:28 UTC (permalink / raw)
To: Govind Singh, bjorn.andersson
Cc: kvalo, davem, netdev, linux-wireless, linux-kernel, ath10k
In-Reply-To: <20180612124403.GA26986@centauri.lan>
On Tue, Jun 12, 2018 at 02:44:03PM +0200, Niklas Cassel wrote:
> On Tue, Jun 12, 2018 at 06:02:48PM +0530, Govind Singh wrote:
> > On 2018-06-12 17:45, Govind Singh wrote:
> > > -----Original Message-----
> > > From: ath10k <ath10k-bounces@lists.infradead.org> On Behalf Of Niklas
> > > Cassel
> > > Sent: Tuesday, June 12, 2018 5:09 PM
> > > To: Kalle Valo <kvalo@codeaurora.org>; David S. Miller
> > > <davem@davemloft.net>
> > > Cc: Niklas Cassel <niklas.cassel@linaro.org>; netdev@vger.kernel.org;
> > > linux-wireless@vger.kernel.org; linux-kernel@vger.kernel.org;
> > > ath10k@lists.infradead.org
> > > Subject: [PATCH 2/2] ath10k: allow ATH10K_SNOC with COMPILE_TEST
> > >
> > > ATH10K_SNOC builds just fine with COMPILE_TEST, so make that possible.
> > >
> > > Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
> > > ---
> > > drivers/net/wireless/ath/ath10k/Kconfig | 3 ++-
> > > 1 file changed, 2 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/net/wireless/ath/ath10k/Kconfig
> > > b/drivers/net/wireless/ath/ath10k/Kconfig
> > > index 54ff5930126c..6572a43590a8 100644
> > > --- a/drivers/net/wireless/ath/ath10k/Kconfig
> > > +++ b/drivers/net/wireless/ath/ath10k/Kconfig
> > > @@ -42,7 +42,8 @@ config ATH10K_USB
> > >
> > > config ATH10K_SNOC
> > > tristate "Qualcomm ath10k SNOC support (EXPERIMENTAL)"
> > > - depends on ATH10K && ARCH_QCOM
> > > + depends on ATH10K
> > > + depends on ARCH_QCOM || COMPILE_TEST
> > > ---help---
> > > This module adds support for integrated WCN3990 chip connected
> > > to system NOC(SNOC). Currently work in progress and will not
> >
> > Thanks Niklas for enabling COMPILE_TEST. With QMI set of
> > changes(https://patchwork.kernel.org/patch/10448183/), we need to enable
> > COMPILE_TEST for
> > QCOM_SCM/QMI_HELPERS which seems broken today. Are you planning to fix the
> > same.
>
>
This patch is good as is.
However, Govind's QMI patch set together with this patch
resulted in build errors.
FTR, these are fixed by:
https://marc.info/?l=linux-kernel&m=152880985402356
https://marc.info/?l=linux-kernel&m=152889452326350
Regards,
Niklas
^ permalink raw reply
* Re: [PATCH net 1/2] ipv4: igmp: use alarmtimer to prevent delayed reports
From: Tejaswi Tanikella @ 2018-06-13 13:32 UTC (permalink / raw)
To: Andrew Lunn; +Cc: netdev, f.fainelli, davem
In-Reply-To: <20180612162818.GA12251@lunn.ch>
On Tue, Jun 12, 2018 at 06:28:18PM +0200, Andrew Lunn wrote:
> On Mon, Jun 11, 2018 at 05:21:05PM +0530, Tejaswi Tanikella wrote:
> > On receiving a IGMPv2/v3 query, based on max_delay set in the header a
> > timer is started to send out a response after a random time within
> > max_delay. If the system then moves into suspend state, Report is
> > delayed until system wakes up.
> >
> > Use a alarmtimer instead of using a timer. Alarmtimer will wake the
> > system up from suspend to send out the IGMP report.
>
> Hi Tejaswi
>
> I think i must be missing something here. If we are suspended, we are
> not receiving multicast frames. If we are not receiving frames, why do
> we need to reply to the query?
>
> Once we resume, i expect we will reply to the next query. You could
> optimise restarting the flow by immediately sending a membership
> report, same as when the setsockopt is used to join the group.
>
> Andrew
Hi Andrew,
I'll try to explain my scenario. This was observed on a arm64 device.
An application registers for a mcast group, and just listens to mcast
packets. The connection is setup and mcast packets are being forwarded
by the router. Multicast packets are sent out every few minutes.
Not a very busy connection.
After some time the router sends out a IGMPv2 query. The max delay in
the header is set to 10s. The system starts a timer to send out the
response at 9s. But the device suspends and wakes up after 60s.
The response is sent out 50s late.
ftrace logs with boottime trace_clock and my igmp_logs:
4740693 kworker/0:3-395 [000] ..s2 4716.425695: igmp_log: skbaddr=ffffffc156fe6600 daddr=224.0.0.1, id=0, rc=4295217721
4740694 kworker/0:3-395 [000] d.s4 4716.425717: timer_start:timer=ffffffc217763140 function=igmp_timer_expire expires=4295218678 [timeout=957] flags=0x00000000
timer set for 9.57 seconds. 957 jiffies
< device suspends >
< wakes up after ~60s >
4781289 <idle>-0 [000] .ns2 4779.170886: timer_expire_entry: timer=ffffffc217763140 function=igmp_timer_expire now=4295218678
4781290 <idle>-0 [000] .ns2 4779.171045: igmp_log: skbaddr=ffffffc1559d0e00 daddr=227.226.228.225, id=0, rc=4295218678
Since the response was delayed, mcast packets are not forwarded by the
router.
My changes use a alarmtimer, this will wake the system up if the timer
expires.
Thanks,
Tejaswi
^ permalink raw reply
* Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
From: Tejaswi Tanikella @ 2018-06-13 13:35 UTC (permalink / raw)
To: Andrew Lunn; +Cc: netdev, f.fainelli, davem
In-Reply-To: <20180612163032.GB12251@lunn.ch>
On Tue, Jun 12, 2018 at 06:30:32PM +0200, Andrew Lunn wrote:
> On Mon, Jun 11, 2018 at 05:22:28PM +0530, Tejaswi Tanikella wrote:
> > Signed-off-by: Tejaswi Tanikella <tejaswit@codeaurora.org>
> > ---
> > include/linux/ktime.h | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/include/linux/ktime.h b/include/linux/ktime.h
> > index 5b9fddb..4881483 100644
> > --- a/include/linux/ktime.h
> > +++ b/include/linux/ktime.h
> > @@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)
> > /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */
> > #define ktime_to_ns(kt) (kt)
> >
> > +/* ktime to jiffies and back */
> > +#define ktime_to_jiffies(kt) nsecs_to_jiffies(kt)
> > +#define jiffies_to_ktime(j) jiffies_to_nsecs(j)
>
> Hi Tejaswi
>
> You should also add some users of these new helpers.
>
> Andrew
Hi Andrew,
I used them in the first patch.
Thanks,
Tejaswi
^ permalink raw reply
* Re: [PATCH net-next 0/10] xfrm: remove flow cache
From: Kristian Evensen @ 2018-06-13 13:43 UTC (permalink / raw)
To: Florian Westphal
Cc: David Miller, Network Development, Steffen Klassert, ilant
In-Reply-To: <20180613124042.zjmfm4rgrvfpgkbs@breakpoint.cc>
Hi,
On Wed, Jun 13, 2018 at 2:40 PM, Florian Westphal <fw@strlen.de> wrote:
> Can you test attached patch?
>
> I'd like to see how much the pcpu cache helps or if it actually hurts
> in your setup.
>
> Subject: [TEST PATCH 4.14.y] xfrm: remove pcpu policy cache
>
> We need to re-evaluate if this still buys anything after indirect calls
> got more expensive (retpolines).
> When pcpu xdst exists, it has to be validated first (which needs
> indirect calls). So even if hit rate is good, it might be cheaper to
> allocate a new xdst entry.
>
> Furthermore, the current xdst cache needs to run with BH off, which
> is also not needed when its removed.
>
> Compile tested only.
Thanks! I will prepare a firmware for one of my devices tonight, start
testing tomorrow and report back when I have some results.
BR,
Kristian
^ permalink raw reply
* [PATCH v2 0/5] can: enable multi-queue for SocketCAN devices
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528224240-30786-1-git-send-email-mark.jonas@de.bosch.com>
Changes in v2:
- use GPIO descriptor API
- make error handling pattern consistent
- use more kernel helper macros and functions
- fix coding style issues
- remove superfluous subsystem name from filename
---
Upon request by Marc Kleine-Budde this patch series does not only
contain our patch to enable enable multi-queue for SocketCAN devices
but also a driver (Companion driver suite) which makes active use of
this feature.
The driver suite implements
- two CAN interfaces
- one generic command interfaces
and offers a SocketCAN as well as a char device interface. The
SocketCAN interface supports multi-queue.
The functionality bases on an external peripheral chip named Companion.
It offers two CAN interfaces, each has 8 prioritized transmit FIFOs as
well as one receive FIFO. Besides CAN, undisclosed additional functions
can be accessed through the char device.
A standard SPI interface with two additional lines for flow control is
used. The Companion chip is the SPI slave.
The driver suite consists of three separate drivers. The following
diagram illustrates the dependencies in layers.
/dev/companion SocketCAN User Space
-------------------------------------------------------------------
+----------------+ +---------------+
| companion-char | | companion-can |
+----------------+ +---------------+
+----------------------------------+
| companion-spi |
+----------------------------------+
+----------------------------------+
| standard SPI subsystem |
+----------------------------------+ Linux Kernel
-------------------------------------------------------------------
| | | | | | Hardware
CS-+ | | | | +-BUSY
CLK--+ | | +---REQUEST
MOSI---+ |
MISO-----+
companion-spi
core.c: handles SPI, sysfs entry and interface to upper layer
protocol-manager.c: handles protocol with the SPI HW
queue-manager.c: handles buffering and packets scheduling
companion-can
makes use of multi-queue support and allows to use tc to configure
the queuing discipline (e.g. mqprio). Together with the SO_PRIORITY
socket option this allows to specify the FIFO a CAN frame shall be
sent to.
companion-char
handles messages to other undisclosed functionality beyond CAN.
Zhu Yi (5):
can: enable multi-queue for SocketCAN devices
spi: implement companion-spi driver
char: implement companion-char driver
can: implement companion-can driver
spi,can,char: add companion DT binding documentation
.../devicetree/bindings/spi/bosch,companion.txt | 82 ++
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 3 +
drivers/char/companion.c | 360 ++++++
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 2 +
drivers/net/can/companion.c | 693 ++++++++++++
drivers/net/can/dev.c | 8 +-
drivers/spi/Kconfig | 2 +
drivers/spi/Makefile | 2 +
drivers/spi/companion/Kconfig | 5 +
drivers/spi/companion/Makefile | 2 +
drivers/spi/companion/core.c | 1185 ++++++++++++++++++++
drivers/spi/companion/protocol-manager.c | 1032 +++++++++++++++++
drivers/spi/companion/protocol-manager.h | 341 ++++++
drivers/spi/companion/protocol.h | 273 +++++
drivers/spi/companion/queue-manager.c | 144 +++
drivers/spi/companion/queue-manager.h | 245 ++++
include/linux/can/dev.h | 7 +-
include/linux/companion.h | 259 +++++
20 files changed, 4656 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
create mode 100644 drivers/char/companion.c
create mode 100644 drivers/net/can/companion.c
create mode 100644 drivers/spi/companion/Kconfig
create mode 100644 drivers/spi/companion/Makefile
create mode 100644 drivers/spi/companion/core.c
create mode 100644 drivers/spi/companion/protocol-manager.c
create mode 100644 drivers/spi/companion/protocol-manager.h
create mode 100644 drivers/spi/companion/protocol.h
create mode 100644 drivers/spi/companion/queue-manager.c
create mode 100644 drivers/spi/companion/queue-manager.h
create mode 100644 include/linux/companion.h
--
2.7.4
^ permalink raw reply
* [PATCH v2 1/5] can: enable multi-queue for SocketCAN devices
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>
From: Zhu Yi <yi.zhu5@cn.bosch.com>
The existing SocketCAN implementation provides alloc_candev() to
allocate a CAN device using a single Tx and Rx queue. This can lead to
priority inversion in case the single Tx queue is already full with low
priority messages and a high priority message needs to be sent while the
bus is fully loaded with medium priority messages.
This problem can be solved by using the existing multi-queue support of
the network subsytem. The commit makes it possible to use multi-queue in
the CAN subsystem in the same way it is used in the Ethernet subsystem
by adding an alloc_candev_mqs() call and accompanying macros. With this
support a CAN device can use multi-queue qdisc (e.g. mqprio) to avoid
the aforementioned priority inversion.
The exisiting functionality of alloc_candev() is the same as before.
CAN devices need to have prioritized multiple hardware queues or are
able to abort waiting for arbitration to make sensible use of
multi-queues.
Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
---
drivers/net/can/dev.c | 8 +++++---
include/linux/can/dev.h | 7 ++++++-
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 365a8cc..ac8270c 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -702,7 +702,8 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb);
/*
* Allocate and setup space for the CAN network device
*/
-struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
+struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
+ unsigned int txqs, unsigned int rxqs)
{
struct net_device *dev;
struct can_priv *priv;
@@ -714,7 +715,8 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
else
size = sizeof_priv;
- dev = alloc_netdev(size, "can%d", NET_NAME_UNKNOWN, can_setup);
+ dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
+ txqs, rxqs);
if (!dev)
return NULL;
@@ -733,7 +735,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
return dev;
}
-EXPORT_SYMBOL_GPL(alloc_candev);
+EXPORT_SYMBOL_GPL(alloc_candev_mqs);
/*
* Free space of the CAN network device
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 61f1cf2..07b73d2 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -142,7 +142,12 @@ u8 can_dlc2len(u8 can_dlc);
/* map the sanitized data length to an appropriate data length code */
u8 can_len2dlc(u8 len);
-struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max);
+struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
+ unsigned int txqs, unsigned int rxqs);
+#define alloc_candev(sizeof_priv, echo_skb_max) \
+ alloc_candev_mqs(sizeof_priv, echo_skb_max, 1, 1)
+#define alloc_candev_mq(sizeof_priv, echo_skb_max, count) \
+ alloc_candev_mqs(sizeof_priv, echo_skb_max, count, count)
void free_candev(struct net_device *dev);
/* a candev safe wrapper around netdev_priv */
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/5] spi: implement companion-spi driver
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>
From: Zhu Yi <yi.zhu5@cn.bosch.com>
The low level companion-spi driver encapsulates the communication
details with the companion processor, and provides interface for
the upper level drivers to access.
Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
drivers/spi/Kconfig | 2 +
drivers/spi/Makefile | 2 +
drivers/spi/companion/Kconfig | 5 +
drivers/spi/companion/Makefile | 2 +
drivers/spi/companion/core.c | 1185 ++++++++++++++++++++++++++++++
drivers/spi/companion/protocol-manager.c | 1032 ++++++++++++++++++++++++++
drivers/spi/companion/protocol-manager.h | 341 +++++++++
drivers/spi/companion/protocol.h | 273 +++++++
drivers/spi/companion/queue-manager.c | 144 ++++
drivers/spi/companion/queue-manager.h | 245 ++++++
include/linux/companion.h | 259 +++++++
11 files changed, 3490 insertions(+)
create mode 100644 drivers/spi/companion/Kconfig
create mode 100644 drivers/spi/companion/Makefile
create mode 100644 drivers/spi/companion/core.c
create mode 100644 drivers/spi/companion/protocol-manager.c
create mode 100644 drivers/spi/companion/protocol-manager.h
create mode 100644 drivers/spi/companion/protocol.h
create mode 100644 drivers/spi/companion/queue-manager.c
create mode 100644 drivers/spi/companion/queue-manager.h
create mode 100644 include/linux/companion.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a75f2a2..8b575ec 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -799,6 +799,8 @@ config SPI_TLE62X0
# Add new SPI protocol masters in alphabetical order above this line
#
+source "drivers/spi/companion/Kconfig"
+
endif # SPI_MASTER
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8e0cda7..ae369d9 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -112,3 +112,5 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
# SPI slave protocol handlers
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
+
+obj-y += companion/
diff --git a/drivers/spi/companion/Kconfig b/drivers/spi/companion/Kconfig
new file mode 100644
index 0000000..490a273
--- /dev/null
+++ b/drivers/spi/companion/Kconfig
@@ -0,0 +1,5 @@
+config COMPANION_SPI
+ tristate "Low level driver for companion communication (Bosch)"
+ depends on SPI
+ help
+ This driver communicates with the companion processor via SPI.
diff --git a/drivers/spi/companion/Makefile b/drivers/spi/companion/Makefile
new file mode 100644
index 0000000..e60e733
--- /dev/null
+++ b/drivers/spi/companion/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_COMPANION_SPI) += companion-spi.o
+companion-spi-objs := core.o protocol-manager.o queue-manager.o
diff --git a/drivers/spi/companion/core.c b/drivers/spi/companion/core.c
new file mode 100644
index 0000000..515e5ec
--- /dev/null
+++ b/drivers/spi/companion/core.c
@@ -0,0 +1,1185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion low level init/core code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+
+#include "protocol-manager.h"
+
+#define DRIVER_NAME "companion-spi"
+
+#define READY_POLL_US 80
+#define READY_POLL_US_GRAN 1
+#define READY_POLL_MS 100
+#define READY_POLL_MS_GRAN 10
+
+/**
+ * struct busy_signal_statistics - spi busy signal statistics
+ * @while_busy_ext: how many times while_busy loop been waited
+ * @while_busy_fail: how many times while_busy been timed out
+ * @until_busy_ext: how many times until_busy loop been waited
+ * @until_busy_fail: how many times until_busy been timed out
+ * @force_started: how many times of force started
+ * @force_started_failure: how many times of force started failure
+ * @ready_failure: how many times of ready failure
+ */
+struct busy_signal_statistics {
+ u32 while_busy_ext;
+ u32 while_busy_fail;
+ u32 until_busy_ext;
+ u32 until_busy_fail;
+ u32 force_started;
+ u32 force_started_failure;
+ u32 ready_failure;
+};
+
+/**
+ * struct companion_spi_priv - companion-spi private data structure
+ * @spi: address of spi device
+ * @task: address of task struct
+ * @wait: wait queue head
+ * @gpiod_request: gpio line connect to request signal
+ * @gpiod_busy: gpio line connect to busy signal
+ * @gpiod_cs: gpio line connect to cs signal
+ * @dump_packet: flag to control dump spi packet
+ * @stats: spi busy signal statistics
+ * @pm: companion protocol manager
+ */
+struct companion_spi_priv {
+ struct spi_device *spi;
+ struct task_struct *task;
+ wait_queue_head_t wait;
+
+ struct gpio_desc *gpiod_request;
+ struct gpio_desc *gpiod_busy;
+ struct gpio_desc *gpiod_cs;
+
+ bool dump_packets;
+ struct busy_signal_statistics stats;
+ struct companion_protocol_manager pm;
+};
+
+/**
+ * companion_io_ops_register() - register companion IO packets handler
+ * @parent: address of the parent device
+ * @ops: address of the IO callbacks
+ * @data: address of the data passed to the IO callbacks
+ */
+int companion_io_ops_register(struct device *parent,
+ struct companion_io_ops *ops,
+ void *data)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_io_ops_register(&priv->pm, ops, data);
+}
+EXPORT_SYMBOL_GPL(companion_io_ops_register);
+
+/**
+ * companion_io_ops_unregister() - unregister companion IO packets handler
+ * @parent: address of the parent device
+ */
+int companion_io_ops_unregister(struct device *parent)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_io_ops_unregister(&priv->pm);
+}
+EXPORT_SYMBOL_GPL(companion_io_ops_unregister);
+
+/**
+ * companion_can_ops_register() - register companion CAN packets handler
+ * @parent: address of the parent device
+ * @port: which CAN port to register
+ * @ops: address of the CAN callbacks
+ * @data: address of the data passed to the CAN callbacks
+ */
+int companion_can_ops_register(struct device *parent,
+ u8 port,
+ struct companion_can_ops *ops,
+ void *data)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_ops_register(&priv->pm, port, ops, data);
+}
+EXPORT_SYMBOL_GPL(companion_can_ops_register);
+
+/**
+ * companion_can_ops_unregister() - unregister companion CAN packets handler
+ * @parent: address of the parent device
+ * @port: which CAN port to unregister
+ */
+int companion_can_ops_unregister(struct device *parent, u8 port)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_ops_unregister(&priv->pm, port);
+}
+EXPORT_SYMBOL_GPL(companion_can_ops_unregister);
+
+/**
+ * companion_io_txq_is_full() - return true if IO tx queue is full
+ * @parent: address of the parent device
+ */
+bool companion_io_txq_is_full(struct device *parent)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return qm_io_txq_is_full(&priv->pm.qm);
+}
+EXPORT_SYMBOL_GPL(companion_io_txq_is_full);
+
+/**
+ * companion_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @parent: address of the parent device
+ */
+bool companion_io_rxq_is_empty(struct device *parent)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return qm_io_rxq_is_empty(&priv->pm.qm);
+}
+EXPORT_SYMBOL_GPL(companion_io_rxq_is_empty);
+
+/**
+ * companion_do_io_tx() - send IO packet
+ * @parent: address of the parent device
+ * @buf: address of the user space buffer to send
+ * @count: number of bytes to copy
+ */
+int companion_do_io_tx(struct device *parent,
+ const char __user *buf,
+ size_t count)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ unsigned int copied;
+ int err;
+ struct companion_packet p;
+
+ if (copy_from_user(p.data, buf, sizeof(p))) {
+ dev_err(parent, "copy from user not succeed in one call\n");
+ return -EFAULT;
+ }
+
+ if (is_can_type(&p))
+ return -EINVAL;
+
+ err = qm_io_txq_in(&priv->pm.qm, buf, count, &copied);
+ if (err) {
+ priv->pm.stats.io_tx_overflows++;
+ return err;
+ }
+
+ wake_up_interruptible(&priv->wait);
+ priv->pm.stats.io_tx++;
+ return copied;
+}
+EXPORT_SYMBOL_GPL(companion_do_io_tx);
+
+/**
+ * companion_do_io_rx() - receive IO packet
+ * @parent: address of the parent device
+ * @buf: address of the user space buffer to receive
+ * @count: number of bytes to copy
+ */
+int companion_do_io_rx(struct device *parent,
+ char __user *buf,
+ size_t count)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ unsigned int copied;
+ int err;
+
+ err = qm_io_rxq_out(&priv->pm.qm, buf, count, &copied);
+ return err ? err : copied;
+}
+EXPORT_SYMBOL_GPL(companion_do_io_rx);
+
+/**
+ * companion_do_can_tx() - send CAN packet
+ * @parent: address of the parent device
+ * @port: which CAN port to send
+ * @prio: priority of the CAN frame
+ * @cf: address of the CAN frame to send
+ */
+int companion_do_can_tx(struct device *parent,
+ u8 port,
+ u8 prio,
+ const struct can_frame *cf)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_data_tx(&priv->pm, port, prio, cf);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_can_tx);
+
+/**
+ * companion_do_can_rx() - receive CAN packet
+ * @parent: address of the parent device
+ * @port: which CAN port to receive
+ * @cf: address of the CAN frame to receive
+ */
+int companion_do_can_rx(struct device *parent,
+ u8 port,
+ struct can_frame *cf)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_data_rx(&priv->pm, port, cf);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_rx);
+
+/**
+ * companion_do_can_err() - receive CAN error packet
+ * @parent: address of the parent device
+ * @port: which CAN port to receive
+ * @bec: address to store CAN error counter
+ * @state: address to store CAN state
+ * @code: address to store CAN error code
+ */
+int companion_do_can_err(struct device *parent,
+ u8 port,
+ struct can_berr_counter *bec,
+ u8 *state,
+ u8 *code)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_err(&priv->pm, port, bec, state, code);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_err);
+
+/**
+ * companion_do_set_can_bittiming() - set CAN bittiming
+ * @parent: address of the parent device
+ * @port: which CAN port to set
+ * @bittiming: address of the bittiming to set
+ */
+int companion_do_set_can_bittiming(struct device *parent,
+ u8 port,
+ const struct can_bittiming *bittiming)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_set_bittiming(&priv->pm, port, bittiming);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ return pm_wait_for_response(&priv->pm, port, bcp_can_bittiming);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_bittiming);
+
+/**
+ * companion_do_set_can_mode() - set CAN mode
+ * @parent: address of the parent device
+ * @port: which CAN port to set
+ * @mode: the CAN mode to set
+ */
+int companion_do_set_can_mode(struct device *parent,
+ u8 port,
+ enum can_mode mode)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_set_mode(&priv->pm, port, mode);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ return pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_mode);
+
+/**
+ * companion_do_set_can_ctrlmode() - set CAN control mode
+ * @parent: address of the parent device
+ * @port: which CAN port to set
+ * @ctrl: the CAN control mode to set
+ */
+int companion_do_set_can_ctrlmode(struct device *parent,
+ u8 port,
+ u32 ctrl)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_set_ctrlmode(&priv->pm, port, ctrl);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ return pm_wait_for_response(&priv->pm, port, bcp_can_mode);
+}
+EXPORT_SYMBOL_GPL(companion_do_set_can_ctrlmode);
+
+/**
+ * companion_do_get_can_status() - get CAN status
+ * @parent: address of the parent device
+ * @port: which CAN port to receive
+ * @bec: address of the CAN error counter to store
+ */
+int companion_do_get_can_status(struct device *parent,
+ u8 port,
+ struct can_berr_counter *bec)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_get_status(&priv->pm, port);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_status);
+ if (err)
+ return err;
+
+ bec->rxerr = priv->pm.rx_err[port];
+ bec->txerr = priv->pm.tx_err[port];
+ return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_status);
+
+/**
+ * companion_do_get_can_txq_status() - get single CAN tx queue status
+ * @parent: address of the parent device
+ * @port: which CAN port to inquiry
+ * @prio: which CAN queue to inquiry
+ * @lost_txq_sync: address of flag to store whether tx queue lost sync
+ */
+int companion_do_get_can_txq_status(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *lost_txq_sync)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ struct companion_protocol_manager *pm = &priv->pm;
+ u8 local, remote;
+ int err;
+
+ if (prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ err = pm_can_get_txq_status(pm, port);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(pm, port, bcp_can_txq_status);
+ if (err)
+ return err;
+
+ local = pm->local_txq[port][prio];
+ remote = pm->remote_txq[port][prio];
+
+ if (local != remote) {
+ *lost_txq_sync = true;
+ pm->stats.can_lost_txq_sync[port][prio]++;
+ } else {
+ *lost_txq_sync = false;
+ pm->stats.can_ack_timeout[port][prio]++;
+ }
+
+ pm->local_txq[port][prio] = remote;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_txq_status);
+
+/**
+ * companion_do_get_can_txq_status_all() - get all CAN tx queue status
+ * @parent: address of the parent device
+ * @port: which CAN port to inquiry
+ */
+int companion_do_get_can_txq_status_all(struct device *parent, u8 port)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+ int err;
+
+ err = pm_can_get_txq_status(&priv->pm, port);
+ if (err)
+ return err;
+
+ wake_up_interruptible(&priv->wait);
+ err = pm_wait_for_response(&priv->pm, port, bcp_can_txq_status);
+ if (err)
+ return err;
+
+ memcpy(priv->pm.local_txq[port],
+ priv->pm.remote_txq[port],
+ BCP_CAN_PRIOS);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(companion_do_get_can_txq_status_all);
+
+/**
+ * companion_do_can_txq_is_full() - inquiry CAN tx queue is full
+ * @parent: address of the parent device
+ * @port: which CAN port to inquiry
+ * @prio: which CAN queue to inquiry
+ * @is_full: address of flag to store is full or not
+ */
+int companion_do_can_txq_is_full(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *is_full)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_txq_is_full(&priv->pm, port, prio, is_full);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_txq_is_full);
+
+/**
+ * companion_do_can_txq_has_space() - inquiry CAN tx queue has space
+ * @parent: address of the parent device
+ * @port: which CAN port to inquiry
+ * @prio: which CAN queue to inquiry
+ * @has_space: address of flag to store has space or not
+ */
+int companion_do_can_txq_has_space(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *has_space)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_txq_has_space(&priv->pm, port, prio, has_space);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_txq_has_space);
+
+/**
+ * companion_do_can_start_tx_timer() - start CAN tx timeout detection
+ * @parent: address of the parent device
+ * @port: which CAN port to start
+ * @prio: which CAN queue to start
+ */
+int companion_do_can_start_tx_timer(struct device *parent,
+ u8 port,
+ u8 prio)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_start_tx_timer(&priv->pm, port, prio);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_start_tx_timer);
+
+/**
+ * companion_do_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @parent: address of the parent device
+ * @port: which CAN port to stop
+ * @prio: which CAN queue to stop
+ */
+int companion_do_can_stop_tx_timer(struct device *parent,
+ u8 port,
+ u8 prio)
+{
+ struct companion_spi_priv *priv = dev_get_drvdata(parent);
+
+ return pm_can_stop_tx_timer(&priv->pm, port, prio);
+}
+EXPORT_SYMBOL_GPL(companion_do_can_stop_tx_timer);
+
+/**
+ * dump_packets_show() - display dump_packets value in sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to encode value
+ */
+static ssize_t dump_packets_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", priv->dump_packets);
+}
+
+/**
+ * dump_packets_store() - store dump_packets value from sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to decode value
+ * @count: number of bytes in the buffer
+ */
+static ssize_t dump_packets_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+ int err;
+
+ err = kstrtobool(buf, &priv->dump_packets);
+ if (err) {
+ dev_err(&spi->dev, "input invalid value: %s\n", buf);
+ return err;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RW(dump_packets);
+
+/**
+ * overflows_show() - display overflows value in sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to encode value
+ */
+static ssize_t overflows_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+ int ret, pos, i, j, total = 0;
+
+ ret = snprintf(buf, PAGE_SIZE, "io\ntx: %u, rx: %u\n\n",
+ priv->pm.stats.io_tx_overflows,
+ priv->pm.stats.io_rx_overflows);
+ pos = ret;
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+ pos += ret;
+
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_tx_overflows[i][j]);
+ total += priv->pm.stats.can_tx_overflows[i][j];
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "\ntx: %u, rx: %u, err: %u\n\n",
+ total,
+ priv->pm.stats.can_rx_overflows[i],
+ priv->pm.stats.can_err_overflows[i]);
+ pos += ret;
+ }
+ return pos;
+}
+static DEVICE_ATTR_RO(overflows);
+
+/**
+ * traffic_show() - display traffic of IO and CAN in sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to encode value
+ */
+static ssize_t traffic_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+ int ret, pos, i, j;
+
+ ret = snprintf(buf, PAGE_SIZE, "io\ntx: %u, rx: %u\n\n",
+ priv->pm.stats.io_tx, priv->pm.stats.io_rx);
+ pos = ret;
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+ pos += ret;
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "tx : ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_tx[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nack success: ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_ack_success[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nack failure: ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_ack_failure[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nlost seq : ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_lost_seq_sync[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nlost txq : ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_lost_txq_sync[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nack timeout: ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_ack_timeout[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nack unexpect:");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.stats.can_ack_unexpect[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "\nrx : %u\nerr : %u\n\n",
+ priv->pm.stats.can_rx[i],
+ priv->pm.stats.can_err[i]);
+ pos += ret;
+ }
+ return pos;
+}
+static DEVICE_ATTR_RO(traffic);
+
+/**
+ * can_space_show() - display CAN queue space in sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to encode value
+ */
+static ssize_t can_space_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+ int i, j, ret, pos = 0;
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "can%u\n", i);
+ pos += ret;
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "local : ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.local_txq[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\nremote: ");
+ pos += ret;
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ ret = snprintf(buf + pos, PAGE_SIZE - pos,
+ "[%u]:%u ", j,
+ priv->pm.remote_txq[i][j]);
+ pos += ret;
+ }
+
+ ret = snprintf(buf + pos, PAGE_SIZE - pos, "\n\n");
+ pos += ret;
+ }
+ return pos;
+}
+static DEVICE_ATTR_RO(can_space);
+
+/**
+ * busy_show() - display busy signal statisitics in sysfs entry
+ * @dev: address of the device associated with sysfs entry
+ * @attr: address of the device attribute
+ * @buf: address of the buffer to encode value
+ */
+static ssize_t busy_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+ return snprintf(buf, PAGE_SIZE,
+ "while_busy_ext : %u\n"
+ "while_busy_fail : %u\n"
+ "until_busy_ext : %u\n"
+ "until_busy_fail : %u\n"
+ "force_started : %u\n"
+ "force_started_failure: %u\n"
+ "ready_failure : %u\n",
+ priv->stats.while_busy_ext,
+ priv->stats.while_busy_fail,
+ priv->stats.until_busy_ext,
+ priv->stats.until_busy_fail,
+ priv->stats.force_started,
+ priv->stats.force_started_failure,
+ priv->stats.ready_failure);
+}
+static DEVICE_ATTR_RO(busy);
+
+static struct attribute *companion_spi_sysfs_attrs[] = {
+ &dev_attr_dump_packets.attr,
+ &dev_attr_overflows.attr,
+ &dev_attr_traffic.attr,
+ &dev_attr_can_space.attr,
+ &dev_attr_busy.attr,
+ NULL
+};
+
+static struct attribute_group companion_spi_attribute_group = {
+ .attrs = companion_spi_sysfs_attrs
+};
+
+/**
+ * slave_has_request() - inquiry spi slave has request
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_has_request(struct companion_spi_priv *priv)
+{
+ return gpiod_get_value_cansleep(priv->gpiod_request) != 0;
+}
+
+/**
+ * slave_is_busy() - inquiry spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_is_busy(struct companion_spi_priv *priv)
+{
+ return gpiod_get_value_cansleep(priv->gpiod_busy) != 0;
+}
+
+/**
+ * slave_is_not_busy() - inquiry spi slave is not busy
+ * @priv: address of companion-spi private data
+ */
+static inline bool slave_is_not_busy(struct companion_spi_priv *priv)
+{
+ return gpiod_get_value_cansleep(priv->gpiod_busy) == 0;
+}
+
+/**
+ * slave_select() - select spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_select(struct companion_spi_priv *priv)
+{
+ gpiod_set_value_cansleep(priv->gpiod_cs, 1);
+}
+
+/**
+ * slave_deselect() - deselect spi slave
+ * @priv: address of companion-spi private data
+ */
+static inline void slave_deselect(struct companion_spi_priv *priv)
+{
+ gpiod_set_value_cansleep(priv->gpiod_cs, 0);
+}
+
+/**
+ * companion_spi_wait_while_busy() - wait while spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_wait_while_busy(struct companion_spi_priv *priv)
+{
+ unsigned int count;
+
+ /*
+ * as short as possible wait while busy polling which shall
+ * succeed most of the times
+ */
+ count = READY_POLL_US / READY_POLL_US_GRAN;
+ do {
+ if (slave_is_not_busy(priv))
+ return 0;
+
+ udelay(READY_POLL_US_GRAN);
+ } while (--count);
+
+ /*
+ * wait while busy polling with sleeping, in case companion
+ * is busy with other things, this shall happen rarely
+ */
+ count = READY_POLL_MS / READY_POLL_MS_GRAN;
+ do {
+ if (slave_is_not_busy(priv)) {
+ priv->stats.while_busy_ext++;
+ dev_info(&priv->spi->dev,
+ "waited long while busy (%u)\n",
+ priv->stats.while_busy_ext);
+ return 0;
+ }
+
+ msleep(READY_POLL_MS_GRAN);
+ } while (--count);
+
+ priv->stats.while_busy_fail++;
+ dev_err(&priv->spi->dev,
+ "time out waiting for busy deassertion (%u)\n",
+ priv->stats.while_busy_fail);
+ return -EBUSY;
+}
+
+/**
+ * companion_spi_wait_until_busy() - wait until spi slave is busy
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_wait_until_busy(struct companion_spi_priv *priv)
+{
+ unsigned int count;
+
+ /*
+ * as short as possible wait until busy polling which shall
+ * succeed most of the times
+ */
+ count = READY_POLL_US / READY_POLL_US_GRAN;
+ do {
+ if (slave_is_busy(priv))
+ return 0;
+
+ udelay(READY_POLL_US_GRAN);
+ } while (--count);
+
+ /*
+ * wait until busy polling with sleeping, in case companion
+ * is busy with other things, this shall happen rarely
+ */
+ count = READY_POLL_MS / READY_POLL_MS_GRAN;
+ do {
+ if (slave_is_busy(priv)) {
+ priv->stats.until_busy_ext++;
+ dev_info(&priv->spi->dev,
+ "waited long until busy (%u)\n",
+ priv->stats.until_busy_ext);
+ return 0;
+ }
+
+ msleep(READY_POLL_MS_GRAN);
+ } while (--count);
+
+ priv->stats.until_busy_fail++;
+ dev_err(&priv->spi->dev,
+ "time out waiting for busy assertion (%u)\n",
+ priv->stats.until_busy_fail);
+ return -EBUSY;
+}
+
+/**
+ * companion_spi_cpu_to_be32() - convert companion packet to big endian 32 bit
+ * @buf: address of the packet to convert
+ */
+static void companion_spi_cpu_to_be32(char *buf)
+{
+ u32 *buf32 = (u32 *)buf;
+ int i;
+
+ for (i = 0; i < (BCP_PACKET_SIZE / sizeof(u32)); i++, buf32++)
+ *buf32 = cpu_to_be32(*buf32);
+}
+
+/**
+ * companion_spi_be32_to_cpu() - convert companion packet from big endian 32 bit
+ * @buf: address of the packet to convert
+ */
+static void companion_spi_be32_to_cpu(char *buf)
+{
+ u32 *buf32 = (u32 *)buf;
+ int i;
+
+ for (i = 0; i < (BCP_PACKET_SIZE / sizeof(u32)); i++, buf32++)
+ *buf32 = be32_to_cpu(*buf32);
+}
+
+/**
+ * companion_spi_transceive() - transceive spi message
+ * @priv: address of companion-spi private data
+ * @message: address of the spi message to transceive
+ * @transfer: address of the spi transfer
+ */
+static void companion_spi_transceive(struct companion_spi_priv *priv,
+ struct spi_message *message,
+ struct spi_transfer *transfer)
+{
+ const struct companion_packet *p;
+ int err;
+
+ if (priv->dump_packets) {
+ p = transfer->tx_buf;
+ dump_packet(p, KERN_INFO, DRIVER_NAME" Tx: ");
+ }
+
+ companion_spi_cpu_to_be32((char *)transfer->tx_buf);
+
+ spi_message_init_with_transfers(message, transfer, 1);
+
+ err = companion_spi_wait_while_busy(priv);
+
+ slave_select(priv);
+
+ if (err) {
+ priv->stats.force_started++;
+ dev_err(&priv->spi->dev,
+ "force started transfer (%u)\n",
+ priv->stats.force_started);
+
+ /* wait slave to pull up busy line in case force started */
+ err = companion_spi_wait_while_busy(priv);
+ if (err) {
+ priv->stats.force_started_failure++;
+ dev_err(&priv->spi->dev,
+ "force started failed, continuing (%u)\n",
+ priv->stats.force_started_failure);
+ }
+ }
+
+ err = companion_spi_wait_until_busy(priv);
+ if (err) {
+ priv->stats.ready_failure++;
+ dev_err(&priv->spi->dev,
+ "started transfer in case not ready (%u)\n",
+ priv->stats.ready_failure);
+ }
+
+ err = spi_sync(priv->spi, message);
+ if (err)
+ dev_err(&priv->spi->dev,
+ "sending spi message failed: %d\n",
+ message->status);
+
+ slave_deselect(priv);
+
+ companion_spi_be32_to_cpu(transfer->rx_buf);
+
+ if (priv->dump_packets) {
+ p = transfer->rx_buf;
+ dump_packet(p, KERN_INFO, DRIVER_NAME" Rx: ");
+ }
+}
+
+/**
+ * companion_spi_request_irq() - irq handler of request signal
+ * @irq: irq number of request signal
+ * @data: address of user supplied data for irq handler
+ */
+static irqreturn_t companion_spi_request_irq(int irq, void *data)
+{
+ struct companion_spi_priv *priv = data;
+
+ wake_up_interruptible(&priv->wait);
+ return IRQ_HANDLED;
+}
+
+/**
+ * companion_spi_thread() - main thread to drive spi communication
+ * @data: address of user supplied data for thread
+ */
+static int companion_spi_thread(void *data)
+{
+ struct companion_spi_priv *priv = data;
+ struct companion_packet tx_packet;
+ struct companion_packet rx_packet;
+ struct spi_message message;
+ struct spi_transfer transfer;
+
+ memset(&transfer, 0, sizeof(transfer));
+ transfer.tx_buf = tx_packet.data;
+ transfer.rx_buf = rx_packet.data;
+ transfer.len = sizeof(struct companion_packet);
+ transfer.cs_change = 0;
+ transfer.bits_per_word = 32;
+
+ do {
+ if (wait_event_interruptible(priv->wait,
+ kthread_should_stop() ||
+ slave_has_request(priv) ||
+ qm_has_tx_data(&priv->pm.qm)))
+ continue;
+
+ if (kthread_should_stop())
+ break;
+
+ pm_prepare_tx(&priv->pm, &tx_packet);
+ companion_spi_transceive(priv, &message, &transfer);
+ pm_on_tx_done(&priv->pm);
+ pm_on_rx_done(&priv->pm, &rx_packet);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static const struct of_device_id companion_spi_of_match[] = {
+ { .compatible = "bosch,companion-spi", .data = NULL, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_spi_of_match);
+
+/**
+ * companion_spi_parse_dt() - parse device tree
+ * @priv: address of companion-spi private data
+ */
+static int companion_spi_parse_dt(struct companion_spi_priv *priv)
+{
+ struct device *dev = &priv->spi->dev;
+ struct device_node *np = dev->of_node;
+ int err;
+
+ if (!np) {
+ dev_err(dev, "no device tree data\n");
+ return -EINVAL;
+ }
+
+ priv->gpiod_request = devm_gpiod_get(dev, "request", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_request)) {
+ err = PTR_ERR(priv->gpiod_request);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "invalid 'request-gpios':%d\n", err);
+ return err;
+ }
+
+ priv->gpiod_busy = devm_gpiod_get(dev, "busy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_busy)) {
+ err = PTR_ERR(priv->gpiod_busy);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "invalid 'busy-gpios':%d\n", err);
+ return err;
+ }
+
+ priv->gpiod_cs = devm_gpiod_get(dev, "cs", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_cs)) {
+ err = PTR_ERR(priv->gpiod_cs);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "invalid 'cs-gpios':%d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * companion_spi_probe() - probe callback
+ * @spi: address of the spi device
+ */
+static int companion_spi_probe(struct spi_device *spi)
+{
+ struct companion_spi_priv *priv;
+ u8 null_packet[BCP_PACKET_SIZE] = {0};
+ int err;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->spi = spi;
+ init_waitqueue_head(&priv->wait);
+ pm_init(&priv->pm);
+
+ err = companion_spi_parse_dt(priv);
+ if (err)
+ return err;
+
+ spi->mode = SPI_MODE_1;
+
+ err = spi_setup(spi);
+ if (err) {
+ dev_err(&spi->dev, "spi_setup() returns: %d\n", err);
+ return err;
+ }
+
+ err = spi_write(spi, null_packet, sizeof(null_packet));
+ if (err) {
+ dev_err(&spi->dev, "dummy transfer failed: %d\n", err);
+ return err;
+ }
+
+ spi_set_drvdata(spi, priv);
+
+ err = sysfs_create_group(&spi->dev.kobj,
+ &companion_spi_attribute_group);
+ if (err) {
+ dev_err(&spi->dev, "sysfs_create_group() returns: %d\n", err);
+ return err;
+ }
+
+ priv->task = kthread_run(companion_spi_thread, priv, DRIVER_NAME);
+ if (!priv->task)
+ return -EIO;
+
+ err = devm_request_irq(&spi->dev,
+ gpiod_to_irq(priv->gpiod_request),
+ companion_spi_request_irq,
+ IRQF_TRIGGER_FALLING,
+ "companion-spi-request",
+ priv);
+ if (err)
+ return -ENODEV;
+
+ return of_platform_populate(spi->dev.of_node, NULL, NULL, &spi->dev);
+}
+
+/**
+ * companion_spi_remove() - remove callback
+ * @spi: address of the spi device
+ */
+static int companion_spi_remove(struct spi_device *spi)
+{
+ struct companion_spi_priv *priv = spi_get_drvdata(spi);
+
+ qm_reset(&priv->pm.qm);
+ kthread_stop(priv->task);
+ sysfs_remove_group(&spi->dev.kobj, &companion_spi_attribute_group);
+ of_platform_depopulate(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver companion_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(companion_spi_of_match),
+ },
+ .probe = companion_spi_probe,
+ .remove = companion_spi_remove,
+};
+module_spi_driver(companion_spi_driver);
+
+MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>");
+MODULE_DESCRIPTION("Companion low level init/core code");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/companion/protocol-manager.c b/drivers/spi/companion/protocol-manager.c
new file mode 100644
index 0000000..1b09c27
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.c
@@ -0,0 +1,1032 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol manager code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#include "protocol-manager.h"
+
+#define PM_RESPONSE_TIMEOUT HZ
+#define PM_CAN_TX_TIMEOUT msecs_to_jiffies(30000)
+
+/**
+ * struct companion_filter - companion packet filter
+ * @node: filter list node
+ * @match: address of match callback
+ * @process: address of process callback
+ */
+struct companion_filter {
+ struct list_head node;
+ bool (*match)(const struct companion_packet *p);
+ void (*process)(struct companion_protocol_manager *pm,
+ const struct companion_packet *p);
+};
+
+/**
+ * null_match() - match null packet
+ * @p: address of the packet to handle
+ */
+static bool null_match(const struct companion_packet *p)
+{
+ return is_null_type(p);
+}
+
+/**
+ * io_match() - match IO packet
+ * @p: address of the packet to handle
+ */
+static bool io_match(const struct companion_packet *p)
+{
+ return is_io_type(p);
+}
+
+/**
+ * io_process() - process IO packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void io_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ if (qm_io_rxq_in(&pm->qm, p)) {
+ down_read(&pm->io_lock);
+ if (pm->io_ops && pm->io_ops->on_rx_done)
+ pm->io_ops->on_rx_done(pm->io_data);
+ up_read(&pm->io_lock);
+
+ pm->stats.io_rx++;
+ } else {
+ pm->stats.io_rx_overflows++;
+ }
+}
+
+/**
+ * can_data_match() - match CAN data packet
+ * @p: address of the packet to handle
+ */
+static bool can_data_match(const struct companion_packet *p)
+{
+ return ((const struct can_data_frame *)p)->type == BCP_CAN_DATA;
+}
+
+/**
+ * can_data_process() - process CAN data packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_data_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_data_frame *)p)->port - 1;
+
+ if (port >= BCP_CAN_PORTS)
+ return;
+
+ if (qm_can_rxq_in(&pm->qm, p, port)) {
+ down_read(&pm->can_lock[port]);
+ if (pm->can_ops[port] && pm->can_ops[port]->on_rx_done)
+ pm->can_ops[port]->on_rx_done(pm->can_data[port]);
+ up_read(&pm->can_lock[port]);
+
+ pm->stats.can_rx[port]++;
+ } else {
+ pm->stats.can_rx_overflows[port]++;
+ }
+}
+
+/**
+ * can_bittiming_match() - match CAN bittiming packet
+ * @p: address of the packet to handle
+ */
+static bool can_bittiming_match(const struct companion_packet *p)
+{
+ return ((const struct can_bittiming_response *)p)->type ==
+ BCP_CAN_BITTIMING;
+}
+
+/**
+ * can_bittiming_process() - process CAN bittiming packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_bittiming_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_bittiming_response *)p)->port - 1;
+ u8 status = ((const struct can_bittiming_response *)p)->status;
+
+ if (port >= BCP_CAN_PORTS)
+ return;
+
+ if (status == BCP_STATUS_SUCCESS) {
+ pm->response[port][bcp_can_bittiming] = 0;
+ pm->stats.can_ack_success[port][0]++;
+ } else {
+ pm->response[port][bcp_can_bittiming] = -EINVAL;
+ pm->stats.can_ack_failure[port][0]++;
+ }
+
+ if (test_and_clear_bit(bcp_can_bittiming, &pm->flags[port]))
+ wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_mode_match() - match CAN mode packet
+ * @p: address of the packet to handle
+ */
+static bool can_mode_match(const struct companion_packet *p)
+{
+ return ((const struct can_mode_response *)p)->type ==
+ BCP_CAN_MODE;
+}
+
+/**
+ * can_mode_process() - process CAN mode packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_mode_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_mode_response *)p)->port - 1;
+ u8 status = ((const struct can_mode_response *)p)->status;
+
+ if (port >= BCP_CAN_PORTS)
+ return;
+
+ if (status == BCP_STATUS_SUCCESS) {
+ pm->response[port][bcp_can_mode] = 0;
+ pm->stats.can_ack_success[port][0]++;
+ } else {
+ pm->response[port][bcp_can_mode] = -EINVAL;
+ pm->stats.can_ack_failure[port][0]++;
+ }
+
+ if (test_and_clear_bit(bcp_can_mode, &pm->flags[port]))
+ wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_status_match() - match CAN status packet
+ * @p: address of the packet to handle
+ */
+static bool can_status_match(const struct companion_packet *p)
+{
+ return ((const struct can_status_response *)p)->type ==
+ BCP_CAN_STATUS;
+}
+
+/**
+ * can_status_process() - process CAN status packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_status_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_status_response *)p)->port - 1;
+ u8 rx_err = ((const struct can_status_response *)p)->rx_err;
+ u8 tx_err = ((const struct can_status_response *)p)->tx_err;
+ u8 status = ((const struct can_status_response *)p)->status;
+
+ if (port >= BCP_CAN_PORTS)
+ return;
+
+ if (status == BCP_STATUS_SUCCESS) {
+ pm->response[port][bcp_can_status] = 0;
+ pm->rx_err[port] = rx_err;
+ pm->tx_err[port] = tx_err;
+
+ if (test_bit(bcp_can_status, &pm->flags[port])) {
+ pm->stats.can_ack_success[port][0]++;
+ goto polling_out;
+ }
+
+ if (qm_can_err_in(&pm->qm, p, port)) {
+ down_read(&pm->can_lock[port]);
+ if (pm->can_ops[port] && pm->can_ops[port]->on_error)
+ pm->can_ops[port]->on_error(pm->can_data[port]);
+ up_read(&pm->can_lock[port]);
+
+ pm->stats.can_err[port]++;
+ } else {
+ pm->stats.can_err_overflows[port]++;
+ }
+ } else {
+ pm->response[port][bcp_can_status] = -EINVAL;
+ if (test_bit(bcp_can_status, &pm->flags[port]))
+ pm->stats.can_ack_failure[port][0]++;
+ }
+
+polling_out:
+ if (test_and_clear_bit(bcp_can_status, &pm->flags[port]))
+ wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * can_tx_ack_match() - match CAN tx acknowledge packet
+ * @p: address of the packet to handle
+ */
+static bool can_tx_ack_match(const struct companion_packet *p)
+{
+ return ((const struct can_tx_acknowledge *)p)->type ==
+ BCP_CAN_TX_ACK;
+}
+
+/**
+ * can_tx_ack_process() - process CAN tx acknowledge packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_tx_ack_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_tx_acknowledge *)p)->port - 1;
+ u8 prio = ((const struct can_tx_acknowledge *)p)->prio;
+ u8 sequence = ((const struct can_tx_acknowledge *)p)->sequence;
+ u8 status = ((const struct can_tx_acknowledge *)p)->status;
+ u8 space = ((const struct can_tx_acknowledge *)p)->space;
+ bool lost_seq = false;
+
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return;
+
+ /* local_txq will be decreased after kernel sent data to companion,
+ * remote_txq will be increased after companion send ack to kernel,
+ * so local_txq < remote_txq should be guaranteed. Otherwise, kernel
+ * received unexpected ack, hence ignore it.
+ */
+ pm->remote_txq[port][prio] = space;
+ if (pm->local_txq[port][prio] >= space) {
+ pm->stats.can_ack_unexpect[port][prio]++;
+ return;
+ }
+
+ pm->local_txq[port][prio]++;
+ pm->sequence[port][prio]++;
+ if (pm->sequence[port][prio] != sequence) {
+ lost_seq = true;
+ pm->sequence[port][prio] = sequence;
+ pm->stats.can_lost_seq_sync[port][prio]++;
+ }
+
+ down_read(&pm->can_lock[port]);
+ if (pm->can_ops[port] && pm->can_ops[port]->on_tx_done)
+ pm->can_ops[port]->on_tx_done(pm->can_data[port],
+ prio,
+ lost_seq,
+ status == BCP_STATUS_SUCCESS);
+ up_read(&pm->can_lock[port]);
+
+ if (status == BCP_STATUS_SUCCESS)
+ pm->stats.can_ack_success[port][prio]++;
+ else
+ pm->stats.can_ack_failure[port][prio]++;
+}
+
+/**
+ * can_txq_status_match() - match CAN tx queue status packet
+ * @p: address of the packet to handle
+ */
+static bool can_txq_status_match(const struct companion_packet *p)
+{
+ return ((const struct can_txq_status_response *)p)->type ==
+ BCP_CAN_TX_QUEUE_STATUS;
+}
+
+/**
+ * can_txq_status_process() - process CAN tx queue status packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void can_txq_status_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ u8 port = ((const struct can_txq_status_response *)p)->port - 1;
+ const u8 *space = ((const struct can_txq_status_response *)p)->space;
+ u8 status = ((const struct can_txq_status_response *)p)->status;
+
+ if (port >= BCP_CAN_PORTS)
+ return;
+
+ if (status == BCP_STATUS_SUCCESS) {
+ memcpy(pm->remote_txq[port], space, BCP_CAN_PRIOS);
+ pm->response[port][bcp_can_txq_status] = 0;
+ pm->stats.can_ack_success[port][0]++;
+ } else {
+ pm->response[port][bcp_can_txq_status] = -EINVAL;
+ pm->stats.can_ack_failure[port][0]++;
+ }
+
+ if (test_and_clear_bit(bcp_can_txq_status, &pm->flags[port]))
+ wake_up_interruptible_all(&pm->wait[port]);
+}
+
+/**
+ * unknown_match() - match unknown packet
+ * @p: address of the packet to handle
+ */
+static bool unknown_match(const struct companion_packet *p)
+{
+ return true;
+}
+
+/**
+ * unknown_process() - process unknown packet
+ * @pm: address of the protocol manager
+ * @p: address of the packet to handle
+ */
+static void unknown_process(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ dump_packet(p, KERN_ERR, "Unknown packet: ");
+}
+
+static struct companion_filter null_filter = {
+ .node = LIST_HEAD_INIT(null_filter.node),
+ .match = null_match,
+ .process = NULL,
+};
+
+static struct companion_filter io_filter = {
+ .node = LIST_HEAD_INIT(io_filter.node),
+ .match = io_match,
+ .process = io_process,
+};
+
+static struct companion_filter can_data_filter = {
+ .node = LIST_HEAD_INIT(can_data_filter.node),
+ .match = can_data_match,
+ .process = can_data_process,
+};
+
+static struct companion_filter can_bittiming_filter = {
+ .node = LIST_HEAD_INIT(can_bittiming_filter.node),
+ .match = can_bittiming_match,
+ .process = can_bittiming_process,
+};
+
+static struct companion_filter can_mode_filter = {
+ .node = LIST_HEAD_INIT(can_mode_filter.node),
+ .match = can_mode_match,
+ .process = can_mode_process,
+};
+
+static struct companion_filter can_status_filter = {
+ .node = LIST_HEAD_INIT(can_status_filter.node),
+ .match = can_status_match,
+ .process = can_status_process,
+};
+
+static struct companion_filter can_tx_ack_filter = {
+ .node = LIST_HEAD_INIT(can_tx_ack_filter.node),
+ .match = can_tx_ack_match,
+ .process = can_tx_ack_process,
+};
+
+static struct companion_filter can_txq_status_filter = {
+ .node = LIST_HEAD_INIT(can_txq_status_filter.node),
+ .match = can_txq_status_match,
+ .process = can_txq_status_process,
+};
+
+static struct companion_filter unknown_filter = {
+ .node = LIST_HEAD_INIT(unknown_filter.node),
+ .match = unknown_match,
+ .process = unknown_process,
+};
+
+/**
+ * to_timer_data() - helper to access companion_timer_data in work_struct
+ * @ws: address of the work_struct object
+ */
+static struct companion_timer_data *to_timer_data(struct work_struct *ws)
+{
+ return container_of(ws, struct companion_timer_data, work);
+}
+
+/**
+ * pm_can_tx_timeout_callback() - CAN tx timeout callback
+ * @ws: address of the work_struct object
+ */
+static void pm_can_tx_timeout_callback(struct work_struct *ws)
+{
+ struct companion_timer_data *td = to_timer_data(ws);
+ struct companion_protocol_manager *pm = td->pm;
+ u8 port = td->port;
+ u8 prio = td->prio;
+
+ down_read(&pm->can_lock[port]);
+ if (pm->can_ops[port] && pm->can_ops[port]->on_tx_timeout)
+ pm->can_ops[port]->on_tx_timeout(pm->can_data[port], prio);
+ up_read(&pm->can_lock[port]);
+}
+
+/**
+ * pm_can_on_tx_timeout() - CAN tx timeout handler
+ * @tl: address of the timer_list object
+ */
+static void pm_can_on_tx_timeout(struct timer_list *tl)
+{
+ struct companion_timer *ct = from_timer(ct, tl, timer);
+
+ schedule_work(&ct->data.work);
+}
+
+#define CHECK_SIZE(x) \
+ BUILD_BUG_ON(sizeof(struct companion_packet) != sizeof(x))
+
+/**
+ * pm_init() - initialize the protocol manager
+ * @pm: address of the protocol manager to be initialized
+ */
+void pm_init(struct companion_protocol_manager *pm)
+{
+ int i;
+
+ /* sanity check for correct packet size at compile time */
+ CHECK_SIZE(struct can_data_frame);
+ CHECK_SIZE(struct can_bittiming_request);
+ CHECK_SIZE(struct can_bittiming_response);
+ CHECK_SIZE(struct can_mode_request);
+ CHECK_SIZE(struct can_mode_response);
+ CHECK_SIZE(struct can_status_request);
+ CHECK_SIZE(struct can_status_response);
+ CHECK_SIZE(struct can_tx_acknowledge);
+ CHECK_SIZE(struct can_txq_status_request);
+ CHECK_SIZE(struct can_txq_status_response);
+
+
+ init_rwsem(&pm->io_lock);
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ init_rwsem(&pm->can_lock[i]);
+ init_waitqueue_head(&pm->wait[i]);
+ }
+
+ qm_init(&pm->qm);
+
+ INIT_LIST_HEAD(&pm->filters);
+ list_add_tail(&null_filter.node, &pm->filters);
+ list_add_tail(&io_filter.node, &pm->filters);
+ list_add_tail(&can_data_filter.node, &pm->filters);
+ list_add_tail(&can_tx_ack_filter.node, &pm->filters);
+ list_add_tail(&can_bittiming_filter.node, &pm->filters);
+ list_add_tail(&can_mode_filter.node, &pm->filters);
+ list_add_tail(&can_status_filter.node, &pm->filters);
+ list_add_tail(&can_txq_status_filter.node, &pm->filters);
+ list_add_tail(&unknown_filter.node, &pm->filters);
+}
+
+/**
+ * pm_io_ops_register() - register companion IO packets handler
+ * @pm: address of the protocol manager to be registered
+ * @ops: address of the IO packets callback
+ * @data: address of the IO packets callback argument
+ */
+int pm_io_ops_register(struct companion_protocol_manager *pm,
+ struct companion_io_ops *ops,
+ void *data)
+{
+ int result = 0;
+
+ down_write(&pm->io_lock);
+ if (pm->io_ops) {
+ result = -EEXIST;
+ goto out;
+ }
+
+ qm_reset_io(&pm->qm);
+ pm->io_ops = ops;
+ pm->io_data = data;
+
+out:
+ up_write(&pm->io_lock);
+ return result;
+}
+
+/**
+ * pm_io_ops_unregister() - unregister companion IO packets handler
+ * @pm: address of the protocol manager to be unregistered
+ */
+int pm_io_ops_unregister(struct companion_protocol_manager *pm)
+{
+ int result = 0;
+
+ down_write(&pm->io_lock);
+ if (!pm->io_ops) {
+ result = -ENODEV;
+ goto out;
+ }
+
+ pm->io_ops = NULL;
+ pm->io_data = NULL;
+ qm_reset_io(&pm->qm);
+
+out:
+ up_write(&pm->io_lock);
+ return result;
+}
+
+/**
+ * pm_can_ops_register() - register companion CAN packets handler
+ * @pm: address of the protocol manager to be registered
+ * @port: port number of which CAN to be registered
+ * @ops: address of the CAN packets callback
+ * @data: address of the CAN packets callback argument
+ */
+int pm_can_ops_register(struct companion_protocol_manager *pm,
+ u8 port,
+ struct companion_can_ops *ops,
+ void *data)
+{
+ int i, result = 0;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ down_write(&pm->can_lock[port]);
+ if (pm->can_ops[port]) {
+ result = -EEXIST;
+ goto out;
+ }
+
+ qm_reset_can(&pm->qm, port);
+ pm->can_ops[port] = ops;
+ pm->can_data[port] = data;
+
+ for (i = 0; i < BCP_CAN_PRIOS; ++i) {
+ pm->timer[port][i].data.pm = pm;
+ pm->timer[port][i].data.port = port;
+ pm->timer[port][i].data.prio = i;
+ INIT_WORK(&pm->timer[port][i].data.work,
+ pm_can_tx_timeout_callback);
+ timer_setup(&pm->timer[port][i].timer, pm_can_on_tx_timeout, 0);
+ }
+
+out:
+ up_write(&pm->can_lock[port]);
+ return result;
+}
+
+/**
+ * pm_can_ops_unregister() - unregister companion CAN packets handler
+ * @pm: address of the protocol manager to be unregistered
+ * @port: port number of which CAN to be unregistered
+ */
+int pm_can_ops_unregister(struct companion_protocol_manager *pm, u8 port)
+{
+ int i, result = 0;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ down_write(&pm->can_lock[port]);
+ if (!pm->can_ops[port]) {
+ result = -ENODEV;
+ goto out;
+ }
+
+ pm->can_ops[port] = NULL;
+ pm->can_data[port] = NULL;
+ qm_reset_can(&pm->qm, port);
+
+ for (i = 0; i < BCP_CAN_PRIOS; ++i) {
+ del_timer_sync(&pm->timer[port][i].timer);
+ cancel_work_sync(&pm->timer[port][i].data.work);
+ pm->timer[port][i].data.pm = NULL;
+ pm->timer[port][i].data.port = 0;
+ pm->timer[port][i].data.prio = 0;
+ }
+
+out:
+ up_write(&pm->can_lock[port]);
+ return result;
+}
+
+/**
+ * pm_prepare_tx() - prepare tx data
+ * @pm: address of the protocol manager to be used
+ * @p: address of the data to be sent
+ */
+void pm_prepare_tx(struct companion_protocol_manager *pm,
+ struct companion_packet *p)
+{
+ pm->is_io_type = false;
+
+ if (qm_get_tx_data(&pm->qm, p)) {
+ if (is_io_type(p))
+ pm->is_io_type = true;
+ } else {
+ memset(p, BCP_NOOP, sizeof(*p));
+ }
+}
+
+/**
+ * pm_on_tx_done() - handle tx done
+ * @pm: address of the protocol manager to be used
+ */
+void pm_on_tx_done(struct companion_protocol_manager *pm)
+{
+ if (!pm->is_io_type)
+ return;
+
+ down_read(&pm->io_lock);
+ if (pm->io_ops && pm->io_ops->on_tx_done)
+ pm->io_ops->on_tx_done(pm->io_data);
+ up_read(&pm->io_lock);
+}
+
+/**
+ * pm_on_rx_done() - handle rx done
+ * @pm: address of the protocol manager to be used
+ * @p: address of the recevied data
+ */
+void pm_on_rx_done(struct companion_protocol_manager *pm,
+ const struct companion_packet *p)
+{
+ struct companion_filter *filter;
+
+ list_for_each_entry(filter, &pm->filters, node) {
+ if (filter->match && filter->match(p)) {
+ if (filter->process)
+ filter->process(pm, p);
+
+ break;
+ }
+ }
+}
+
+/**
+ * pm_can_data_tx() - send CAN data according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be sent
+ * @prio: priority of the data to be sent
+ * @cf: the raw CAN frame to be send
+ */
+int pm_can_data_tx(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ const struct can_frame *cf)
+{
+ struct can_data_frame p;
+
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ if (pm->local_txq[port][prio] == 0)
+ return -ENOSPC;
+
+ p.type = BCP_CAN_DATA;
+ p.port = port + 1;
+ p.prio = prio;
+ p.dlc = cf->can_dlc;
+ p.id = cf->can_id;
+ memcpy(p.data, cf->data, sizeof(cf->data));
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ prio)) {
+ pm->stats.can_tx_overflows[port][prio]++;
+ return -ENOSPC;
+ }
+
+ pm->local_txq[port][prio]--;
+
+ pm->stats.can_tx[port][prio]++;
+ return 0;
+}
+
+/**
+ * pm_can_data_rx() - receive CAN data according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @cf: address of the raw CAN frame to be copied
+ */
+int pm_can_data_rx(struct companion_protocol_manager *pm,
+ u8 port,
+ struct can_frame *cf)
+{
+ struct can_data_frame p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ if (!qm_can_rxq_out(&pm->qm, (struct companion_packet *)&p, port))
+ return -EIO;
+
+ cf->can_id = p.id;
+ cf->can_dlc = p.dlc;
+ memcpy(cf->data, p.data, sizeof(cf->data));
+ return 0;
+}
+
+/**
+ * pm_can_err() - receive CAN error according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @bec: address of the error counter to be copied
+ * @state: address of the error state to be copied
+ * @code: address of the error code to be copied
+ */
+int pm_can_err(struct companion_protocol_manager *pm,
+ u8 port,
+ struct can_berr_counter *berr,
+ u8 *state,
+ u8 *code)
+{
+ struct can_status_response p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ if (!qm_can_err_out(&pm->qm, (struct companion_packet *)&p, port))
+ return -EIO;
+
+ berr->rxerr = p.rx_err;
+ berr->txerr = p.tx_err;
+ *state = p.state;
+ *code = p.code;
+ return 0;
+}
+
+/**
+ * pm_wait_for_response() - wait for CAN packets response
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be wait
+ * @flag: flag to be wait
+ */
+int pm_wait_for_response(struct companion_protocol_manager *pm,
+ u8 port,
+ enum bcp_can_flag flag)
+{
+ unsigned long *flags = &pm->flags[port];
+ long ret;
+
+ if (test_bit(flag, flags)) {
+ ret = wait_event_interruptible_timeout(pm->wait[port],
+ !test_bit(flag, flags),
+ PM_RESPONSE_TIMEOUT);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0)
+ return -ETIMEDOUT;
+ }
+
+ return pm->response[port][flag];
+}
+
+/**
+ * pm_can_set_bittiming() - set CAN bittiming according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int pm_can_set_bittiming(struct companion_protocol_manager *pm,
+ u8 port,
+ const struct can_bittiming *bittiming)
+{
+ struct can_bittiming_request p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_BITTIMING;
+ p.port = port + 1;
+ p.prescaler = bittiming->brp;
+ p.prop_seg = bittiming->prop_seg;
+ p.phase_seg1 = bittiming->phase_seg1;
+ p.phase_seg2 = bittiming->phase_seg2;
+ p.sjw = bittiming->sjw;
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ 0)) {
+ pm->stats.can_tx_overflows[port][0]++;
+ return -ENOSPC;
+ }
+
+ set_bit(bcp_can_bittiming, &pm->flags[port]);
+ pm->stats.can_tx[port][0]++;
+ return 0;
+}
+
+/**
+ * pm_can_set_mode() - set CAN mode according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @mode: the mode to be set
+ */
+int pm_can_set_mode(struct companion_protocol_manager *pm,
+ u8 port,
+ enum can_mode mode)
+{
+ struct can_mode_request p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_MODE;
+ p.port = port + 1;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ p.mode = BCP_CAN_MODE_NORMAL;
+ break;
+
+ case CAN_MODE_STOP:
+ p.mode = BCP_CAN_MODE_OFF;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ 0)) {
+ pm->stats.can_tx_overflows[port][0]++;
+ return -ENOSPC;
+ }
+
+ set_bit(bcp_can_mode, &pm->flags[port]);
+ pm->stats.can_tx[port][0]++;
+ return 0;
+}
+
+/**
+ * pm_can_set_ctrlmode() - set CAN control mode according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @ctrl: the control mode to be set
+ */
+int pm_can_set_ctrlmode(struct companion_protocol_manager *pm,
+ u8 port,
+ u32 ctrl)
+{
+ struct can_mode_request p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ if (!(ctrl & CAN_CTRLMODE_LISTENONLY))
+ return -EOPNOTSUPP;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_MODE;
+ p.port = port + 1;
+ p.mode = BCP_CAN_MODE_LISTEN;
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ 0)) {
+ pm->stats.can_tx_overflows[port][0]++;
+ return -ENOSPC;
+ }
+
+ set_bit(bcp_can_mode, &pm->flags[port]);
+ pm->stats.can_tx[port][0]++;
+ return 0;
+}
+
+/**
+ * pm_can_get_status() - get CAN status according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_status(struct companion_protocol_manager *pm, u8 port)
+{
+ struct can_status_request p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_STATUS;
+ p.port = port + 1;
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ 0)) {
+ pm->stats.can_tx_overflows[port][0]++;
+ return -ENOSPC;
+ }
+
+ set_bit(bcp_can_status, &pm->flags[port]);
+ pm->stats.can_tx[port][0]++;
+ return 0;
+}
+
+/**
+ * pm_can_get_txq_status() - get CAN tx queue status according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_txq_status(struct companion_protocol_manager *pm, u8 port)
+{
+ struct can_txq_status_request p;
+
+ if (port >= BCP_CAN_PORTS)
+ return -EINVAL;
+
+ memset(&p, 0, sizeof(p));
+ p.type = BCP_CAN_TX_QUEUE_STATUS;
+ p.port = port + 1;
+
+ if (!qm_can_txq_in(&pm->qm,
+ (struct companion_packet *)&p,
+ port,
+ 0)) {
+ pm->stats.can_tx_overflows[port][0]++;
+ return -ENOSPC;
+ }
+
+ set_bit(bcp_can_txq_status, &pm->flags[port]);
+ pm->stats.can_tx[port][0]++;
+ return 0;
+}
+
+/**
+ * pm_can_txq_is_full() - inquiry CAN tx queue is full
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int pm_can_txq_is_full(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ bool *is_full)
+{
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ *is_full = (pm->local_txq[port][prio] == 0);
+ return 0;
+}
+
+/**
+ * pm_can_txq_has_space() - inquiry CAN tx queue has space
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @has_space: address of the has space result to be copied
+ */
+int pm_can_txq_has_space(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ bool *has_space)
+{
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ *has_space = (pm->local_txq[port][prio] > 0);
+ return 0;
+}
+
+/**
+ * pm_can_start_tx_timer() - start CAN tx timeout detection
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_start_tx_timer(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio)
+{
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ mod_timer(&pm->timer[port][prio].timer, jiffies + PM_CAN_TX_TIMEOUT);
+ return 0;
+}
+
+/**
+ * pm_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_stop_tx_timer(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio)
+{
+ if (port >= BCP_CAN_PORTS || prio >= BCP_CAN_PRIOS)
+ return -EINVAL;
+
+ del_timer_sync(&pm->timer[port][prio].timer);
+ return 0;
+}
diff --git a/drivers/spi/companion/protocol-manager.h b/drivers/spi/companion/protocol-manager.h
new file mode 100644
index 0000000..2cf10fa
--- /dev/null
+++ b/drivers/spi/companion/protocol-manager.h
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol manager code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#ifndef _BOSCH_COMPANION_PROTOCOL_MANAGER_H
+#define _BOSCH_COMPANION_PROTOCOL_MANAGER_H
+
+#include <linux/can/dev.h>
+#include <linux/list.h>
+#include <linux/rwsem.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/companion.h>
+#include "queue-manager.h"
+
+/**
+ * enum bcp_can_flag - companion CAN packet event flag
+ */
+enum bcp_can_flag {
+ bcp_can_bittiming,
+ bcp_can_mode,
+ bcp_can_status,
+ bcp_can_txq_status,
+
+ bcp_can_max
+};
+
+/**
+ * struct companion_statistics - statistics information of companion
+ * @io_tx_overflows: counter of IO packets transmit overflow
+ * @io_rx_overflows: counter of IO packets receive overflow
+ * @can_tx_overflows: counter of CAN packets transmit overflow
+ * @can_rx_overflows: counter of CAN packets receive overflow
+ * @can_err_overflows: counter of CAN error packets receive overflow
+ * @io_tx: counter of IO packets transmitted
+ * @io_rx: counter of IO packets received
+ * @can_tx: counter of CAN packets transmitted
+ * @can_ack_success: counter of CAN response success packets received
+ * @can_ack_failure: counter of CAN response failure packets received
+ * @can_rx: counter of CAN packets received
+ * @can_err: counter of CAN error packets received
+ * @can_lost_seq_sync: counter of CAN packets sequence lost sync
+ * @can_lost_txq_sync: counter of CAN tx queue status lost sync
+ * @can_ack_timeout: counter of CAN tx ack timeout
+ * @can_ack_unexpect: counter of CAN unexpected tx ack
+ */
+struct companion_statistics {
+ u32 io_tx_overflows;
+ u32 io_rx_overflows;
+ u32 can_tx_overflows[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_rx_overflows[BCP_CAN_PORTS];
+ u32 can_err_overflows[BCP_CAN_PORTS];
+
+ u32 io_tx;
+ u32 io_rx;
+ u32 can_tx[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_ack_success[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_ack_failure[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_rx[BCP_CAN_PORTS];
+ u32 can_err[BCP_CAN_PORTS];
+ u32 can_lost_seq_sync[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_lost_txq_sync[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_ack_timeout[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u32 can_ack_unexpect[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+};
+
+struct companion_protocol_manager;
+/**
+ * struct companion_timer_data - encapsulate data passed to timer
+ * @pm: companion protocol manager
+ * @port: port number of which CAN associated with the timer
+ * @prio: priority of which CAN tx queue associated with the timer
+ * @work: work for execute timeout callback in thread context
+ */
+struct companion_timer_data {
+ struct companion_protocol_manager *pm;
+ u8 port;
+ u8 prio;
+ struct work_struct work;
+};
+
+/**
+ * struct companion_timer - encapsulate timer and data
+ * @timer: timer_list object for timeout
+ * @data: data associated with the timer
+ */
+struct companion_timer {
+ struct timer_list timer;
+ struct companion_timer_data data;
+};
+
+/**
+ * struct companion_protocol_manager - encapsulate companion protocol handling
+ * @io_ops: callback of IO packet handling
+ * @io_data: callback argument of io_ops
+ * @io_lock: lock to protect IO callback
+ * @can_ops: callback of CAN packet handling
+ * @can_data: callback argument of can_ops
+ * @can_lock: lock to protect CAN callback
+ * @wait: wait queue head for CAN packet response
+ * @flags: event flags for CAN packet response
+ * @response: response result of each CAN packet event flag
+ * @rx_err: receive error counter of CAN port
+ * @tx_err: transmit error counter of CAN port
+ * @sequence: sequence counter of each CAN tx queue
+ * @remote_txq: CAN tx queue space status at remote
+ * @local_txq: CAN tx queue space status at local
+ * @timer: timer for detect CAN tx ack timeout for each CAN tx queue
+ * @stats: statistics information of companion
+ * @is_io_type: flag to record the sent packet is IO type or not
+ * @qm: queue manager
+ * @filters: filter list to handle receveid companion packets
+ */
+struct companion_protocol_manager {
+ struct companion_io_ops *io_ops;
+ void *io_data;
+ struct rw_semaphore io_lock;
+ struct companion_can_ops *can_ops[BCP_CAN_PORTS];
+ void *can_data[BCP_CAN_PORTS];
+ struct rw_semaphore can_lock[BCP_CAN_PORTS];
+ wait_queue_head_t wait[BCP_CAN_PORTS];
+ unsigned long flags[BCP_CAN_PORTS];
+ int response[BCP_CAN_PORTS][bcp_can_max];
+ u8 rx_err[BCP_CAN_PORTS];
+ u8 tx_err[BCP_CAN_PORTS];
+ u8 sequence[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u8 remote_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ u8 local_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ struct companion_timer timer[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+
+ struct companion_statistics stats;
+ bool is_io_type;
+
+ struct companion_queue_manager qm;
+ struct list_head filters;
+};
+
+/**
+ * pm_init() - initialize the protocol manager
+ * @pm: address of the protocol manager to be initialized
+ */
+void pm_init(struct companion_protocol_manager *pm);
+
+/**
+ * pm_io_ops_register() - register companion IO packets handler
+ * @pm: address of the protocol manager to be registered
+ * @ops: address of the IO packets callback
+ * @data: address of the IO packets callback argument
+ */
+int pm_io_ops_register(struct companion_protocol_manager *pm,
+ struct companion_io_ops *ops,
+ void *data);
+
+/**
+ * pm_io_ops_unregister() - unregister companion IO packets handler
+ * @pm: address of the protocol manager to be unregistered
+ */
+int pm_io_ops_unregister(struct companion_protocol_manager *pm);
+
+/**
+ * pm_can_ops_register() - register companion CAN packets hanler
+ * @pm: address of the protocol manager to be registered
+ * @port: port number of which CAN to be registered
+ * @ops: address of the CAN packets callback
+ * @data: address of the CAN packets callback argument
+ */
+int pm_can_ops_register(struct companion_protocol_manager *pm,
+ u8 port,
+ struct companion_can_ops *ops,
+ void *data);
+
+/**
+ * pm_can_ops_unregister() - unregister companion CAN packets handler
+ * @pm: address of the protocol manager to be unregistered
+ * @port: port number of which CAN to be unregistered
+ */
+int pm_can_ops_unregister(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_prepare_tx() - prepare tx data
+ * @pm: address of the protocol manager to be used
+ * @p: address of the data to be sent
+ */
+void pm_prepare_tx(struct companion_protocol_manager *pm,
+ struct companion_packet *p);
+
+/**
+ * pm_on_tx_done() - handle tx done
+ * @pm: address of the protocol manager to be used
+ */
+void pm_on_tx_done(struct companion_protocol_manager *pm);
+
+/**
+ * pm_on_rx_done() - handle rx done
+ * @pm: address of the protocol manager to be used
+ * @p: address of the recevied data
+ */
+void pm_on_rx_done(struct companion_protocol_manager *pm,
+ const struct companion_packet *p);
+
+/**
+ * pm_can_data_tx() - send CAN data according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be sent
+ * @prio: priority of the data to be sent
+ * @cf: the raw CAN frame to be send
+ */
+int pm_can_data_tx(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ const struct can_frame *cf);
+
+/**
+ * pm_can_data_rx() - receive CAN data according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @cf: address of the raw CAN frame to be copied
+ */
+int pm_can_data_rx(struct companion_protocol_manager *pm,
+ u8 port,
+ struct can_frame *cf);
+
+/**
+ * pm_can_err() - receive CAN error according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be received
+ * @bec: address of the error counter to be copied
+ * @state: address of the error state to be copied
+ * @code: address of the error code to be copied
+ */
+int pm_can_err(struct companion_protocol_manager *pm,
+ u8 port,
+ struct can_berr_counter *bec,
+ u8 *state,
+ u8 *code);
+
+/**
+ * pm_wait_for_response() - wait for CAN packets response
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be wait
+ * @flag: flag to be wait
+ */
+int pm_wait_for_response(struct companion_protocol_manager *pm,
+ u8 port,
+ enum bcp_can_flag flag);
+
+/**
+ * pm_can_set_bittiming() - set CAN bittiming according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int pm_can_set_bittiming(struct companion_protocol_manager *pm,
+ u8 port,
+ const struct can_bittiming *bittiming);
+
+/**
+ * pm_can_set_mode() - set CAN mode according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @mode: the mode to be set
+ */
+int pm_can_set_mode(struct companion_protocol_manager *pm,
+ u8 port,
+ enum can_mode mode);
+
+/**
+ * pm_can_set_ctrlmode() - set CAN control mode according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be set
+ * @ctrl: the control mode to be set
+ */
+int pm_can_set_ctrlmode(struct companion_protocol_manager *pm,
+ u8 port,
+ u32 ctrl);
+
+/**
+ * pm_can_get_status() - get CAN status according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_status(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_can_get_txq_status() - get CAN tx queue status according to protocol
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int pm_can_get_txq_status(struct companion_protocol_manager *pm, u8 port);
+
+/**
+ * pm_can_txq_is_full() - inquiry CAN tx queue is full
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int pm_can_txq_is_full(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ bool *is_full);
+
+/**
+ * pm_can_txq_has_space() - inquiry CAN tx queue has space
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @has_space: address of the has space result to be copied
+ */
+int pm_can_txq_has_space(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio,
+ bool *has_space);
+
+/**
+ * pm_can_start_tx_timer() - start CAN tx timeout detection
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_start_tx_timer(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio);
+
+/**
+ * pm_can_stop_tx_timer() - stop CAN tx timeout detection
+ * @pm: address of the protocol manager to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int pm_can_stop_tx_timer(struct companion_protocol_manager *pm,
+ u8 port,
+ u8 prio);
+#endif
diff --git a/drivers/spi/companion/protocol.h b/drivers/spi/companion/protocol.h
new file mode 100644
index 0000000..7f70ed6
--- /dev/null
+++ b/drivers/spi/companion/protocol.h
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion protocol kernel space API
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#ifndef _BOSCH_COMPANION_PROTOCOL_KERNEL_H
+#define _BOSCH_COMPANION_PROTOCOL_KERNEL_H
+
+#include <linux/kernel.h>
+
+#define BCP_CAN_PORTS 2u
+#define BCP_CAN_PRIOS 8u
+
+/**
+ * BCP type field definitions (CAN)
+ */
+#define BCP_NOOP 0x00u
+#define BCP_CAN_DATA 0x01u
+#define BCP_CAN_BITTIMING 0x03u
+#define BCP_CAN_MODE 0x04u
+#define BCP_CAN_STATUS 0x06u
+#define BCP_CAN_TX_ACK 0x07u
+#define BCP_CAN_TX_QUEUE_STATUS 0x0Fu
+
+/**
+ * BCP status field definitions
+ */
+#define BCP_STATUS_SUCCESS 0x00u
+#define BCP_STATUS_UNKNOWN 0x01u
+#define BCP_STATUS_OTHER 0x02u
+
+/**
+ * BCP packet size definition
+ */
+#define BCP_PACKET_SIZE 16u
+
+/**
+ * struct companion_packet - companion packet general format
+ * @data: contents of the packet
+ */
+struct companion_packet {
+ u8 data[BCP_PACKET_SIZE];
+};
+
+/**
+ * struct can_data_frame - companion can data frame packet
+ * @type: packet type
+ * @port: can port
+ * @prio: priority of the can frame
+ * @dlc: can frame payload in bytes
+ * @id: can frame id
+ * @data: can frame payload
+ */
+struct can_data_frame {
+ u8 type;
+ u8 port;
+ u8 prio;
+ u8 dlc;
+ u32 id;
+ u8 data[8];
+};
+
+/**
+ * struct can_bittiming_request - companion can bittiming request packet
+ * @type: packet type
+ * @port: can port
+ * @prescaler: bitrate prescaler
+ * @prop_seg: propagation segment in TQs
+ * @phase_seg1: phase buffer segment 1 in TQs
+ * @phase_seg2: phase buffer segment 2 in TQs
+ * @sjw: synchronisation jump width in TQs
+ * @reserved: reserved
+ */
+struct can_bittiming_request {
+ u8 type;
+ u8 port;
+ u16 prescaler;
+ u8 prop_seg;
+ u8 phase_seg1;
+ u8 phase_seg2;
+ u8 sjw;
+ u8 reserved[8];
+};
+
+/**
+ * struct can_bittiming_response - companion can bittiming response packet
+ * @type: packet type
+ * @port: can port
+ * @prescaler: bitrate prescaler
+ * @prop_seg: propagation segment in TQs
+ * @phase_seg1: phase buffer segment 1 in TQs
+ * @phase_seg2: phase buffer segment 2 in TQs
+ * @sjw: synchronisation jump width in TQs
+ * @reserved: reserved
+ * @status: process status
+ */
+struct can_bittiming_response {
+ u8 type;
+ u8 port;
+ u16 prescaler;
+ u8 prop_seg;
+ u8 phase_seg1;
+ u8 phase_seg2;
+ u8 sjw;
+ u8 reserved[7];
+ u8 status;
+};
+
+/**
+ * struct can_mode_request - companion can mode request packet
+ * @type: packet type
+ * @port: can port
+ * @mode: can mode
+ * @reserved: reserved
+ */
+struct can_mode_request {
+ u8 type;
+ u8 port;
+ u8 mode;
+ u8 reserved[13];
+};
+#define BCP_CAN_MODE_OFF 0x00u
+#define BCP_CAN_MODE_NORMAL 0x01u
+#define BCP_CAN_MODE_LISTEN 0x02u
+
+/**
+ * struct can_mode_response - companion can mode response packet
+ * @type: packet type
+ * @port: can port
+ * @mode: can mode
+ * @reserved: reserved
+ * @status: process status
+ */
+struct can_mode_response {
+ u8 type;
+ u8 port;
+ u8 mode;
+ u8 reserved[12];
+ u8 status;
+};
+
+/**
+ * struct can_status_request - companion can status request packet
+ * @type: packet type
+ * @port: can port
+ * @reserved: reserved
+ */
+struct can_status_request {
+ u8 type;
+ u8 port;
+ u8 reserved[14];
+};
+
+/**
+ * struct can_status_response - companion can status response packet
+ * @type: packet type
+ * @port: can port
+ * @rx_err: rx error counter
+ * @tx_err: tx error counter
+ * @err1: can controller error status 1
+ * @err2: can controller error status 2
+ * @reserved: reserved
+ * @status: process status
+ */
+struct can_status_response {
+ u8 type;
+ u8 port;
+ u8 rx_err;
+ u8 tx_err;
+ u8 state;
+ u8 code;
+ u8 reserved[9];
+ u8 status;
+};
+
+/**
+ * struct can_tx_acknowledge - companion can tx acknowledge packet
+ * @type: packet type
+ * @port: can port
+ * @prio: priority of the can frame
+ * @dlc: payload length of the can frame
+ * @sequence: monotonic increasing sequence counter of sent can frames
+ * @space: queue space left of this priority
+ * @reserved: reserved
+ * @status: process status
+ */
+struct can_tx_acknowledge {
+ u8 type;
+ u8 port;
+ u8 prio;
+ u8 dlc;
+ u8 sequence;
+ u8 space;
+ u8 reserved[9];
+ u8 status;
+};
+
+/**
+ * struct can_txq_status_request - companion can txq status request packet
+ * @type: packet type
+ * @port: can port
+ * @reserved: reserved
+ */
+struct can_txq_status_request {
+ u8 type;
+ u8 port;
+ u8 reserved[14];
+};
+
+/**
+ * struct can_txq_status_response - companion can txq status response packet
+ * @type: packet type
+ * @port: can port
+ * @space: queue space left of each priority
+ * @reserved: reserved
+ * @status: process status
+ */
+struct can_txq_status_response {
+ u8 type;
+ u8 port;
+ u8 space[8];
+ u8 reserved[5];
+ u8 status;
+};
+
+/**
+ * is_null_type() - return true if the packet is null type
+ * @p: the packet to test
+ */
+static inline bool is_null_type(const struct companion_packet *p)
+{
+ return p->data[0] == BCP_NOOP;
+}
+
+/**
+ * is_io_type() - return true if the packet is io type
+ * @p: the packet to test
+ */
+static inline bool is_io_type(const struct companion_packet *p)
+{
+ return (p->data[0] & 0x80);
+}
+
+/**
+ * is_can_type() - return true if the packet is can type
+ * @p: the packet to test
+ */
+static inline bool is_can_type(const struct companion_packet *p)
+{
+ return !is_io_type(p) && !is_null_type(p);
+}
+
+/**
+ * dump_packet() - dump raw packet data in hexadecimal format
+ * @p: the packet to dump
+ * @level: the log level of the dump
+ * @prefix: the prefix string of the dump
+ */
+static inline void dump_packet(const struct companion_packet *p,
+ const char *level,
+ const char *prefix)
+{
+ print_hex_dump(level, prefix, DUMP_PREFIX_NONE, 16, 1,
+ p->data, sizeof(p->data), false);
+}
+
+#endif
diff --git a/drivers/spi/companion/queue-manager.c b/drivers/spi/companion/queue-manager.c
new file mode 100644
index 0000000..f72696f
--- /dev/null
+++ b/drivers/spi/companion/queue-manager.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion queue management code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#include "queue-manager.h"
+
+/**
+ * qm_init() - initialize all managed queues
+ * @qm: address of the queue manager to be initialized
+ */
+void qm_init(struct companion_queue_manager *qm)
+{
+ int i, j;
+
+ INIT_KFIFO(qm->io_txq.fifo);
+ INIT_KFIFO(qm->io_rxq.fifo);
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ for (j = 0; j < BCP_CAN_PRIOS; ++j)
+ INIT_KFIFO(qm->can_txq[i][j].fifo);
+
+ INIT_KFIFO(qm->can_rxq[i].fifo);
+ INIT_KFIFO(qm->can_err[i].fifo);
+ mutex_init(&qm->can_txq_lock[i]);
+ }
+}
+
+/**
+ * qm_reset() - reset all managed queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset(struct companion_queue_manager *qm)
+{
+ int i;
+
+ qm_reset_io(qm);
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i)
+ qm_reset_can(qm, i);
+}
+
+/**
+ * qm_reset_io() - reset managed IO queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset_io(struct companion_queue_manager *qm)
+{
+ kfifo_reset(&qm->io_txq.fifo);
+ kfifo_reset(&qm->io_rxq.fifo);
+}
+
+/**
+ * qm_reset_can() - reset managed CAN queues
+ * @qm: address of the queue manager to be reset
+ * @port: port number of which CAN queue should be reset
+ */
+void qm_reset_can(struct companion_queue_manager *qm, u8 port)
+{
+ int i;
+
+ for (i = 0; i < BCP_CAN_PRIOS; ++i)
+ kfifo_reset(&qm->can_txq[port][i].fifo);
+
+ kfifo_reset(&qm->can_rxq[port].fifo);
+ kfifo_reset(&qm->can_err[port].fifo);
+}
+
+/**
+ * qm_has_tx_data() - return true if has tx data
+ * @qm: address of the queue manager to be used
+ */
+bool qm_has_tx_data(struct companion_queue_manager *qm)
+{
+ int i, j;
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i)
+ for (j = 0; j < BCP_CAN_PRIOS; ++j)
+ if (!kfifo_is_empty(&qm->can_txq[i][j].fifo))
+ return true;
+
+ return !kfifo_is_empty(&qm->io_txq.fifo);
+}
+
+/*
+ * Define maximum CAN packets can be sent in a row in case there is IO packet
+ * pending or coming, which specifies the minimal bandwidth for IO packets.
+ */
+#define CAN_MAX_IN_A_ROW 8
+
+/**
+ * qm_get_tx_data() - return true if got the tx data
+ * @qm: address of the queue manager to be used
+ * @p: where the data to be copied
+ */
+bool qm_get_tx_data(struct companion_queue_manager *qm,
+ struct companion_packet *p)
+{
+ int i, j;
+
+ /*
+ * Implement the companion packet scheduling algorithm which guarantees
+ * IO packets share minimal 1 / (CAN_MAX_IN_A_ROW + 1) bandwidth, and
+ * the rest bandwidth is shared equally for all CAN ports.
+ *
+ * The purpose is to ensure fairness between all CAN ports and also keep
+ * CAN packets have higher priority than IO packets in general, but
+ * avoid IO packets starvation in case CAN is very busy.
+ *
+ * The bandwidth is not statically allocated, so the active user (IO or
+ * CAN) can use up to 100% bandwidth if there are no other active users.
+ */
+
+ if (qm->io_promoted && qm_io_txq_out(qm, p)) {
+ qm->io_promoted = false;
+ return true;
+ }
+
+ for (i = 0; i < BCP_CAN_PORTS; ++i) {
+ /* ensure fairness for all can ports */
+ qm->can_current++;
+ if (qm->can_current >= BCP_CAN_PORTS)
+ qm->can_current = 0;
+
+ for (j = 0; j < BCP_CAN_PRIOS; ++j) {
+ if (qm_can_txq_out(qm, p, qm->can_current, j)) {
+ qm->can_sched++;
+ if (qm->can_sched >= CAN_MAX_IN_A_ROW) {
+ qm->io_promoted = true;
+ qm->can_sched = 0;
+ }
+ return true;
+ }
+ }
+ }
+
+ return qm_io_txq_out(qm, p);
+}
diff --git a/drivers/spi/companion/queue-manager.h b/drivers/spi/companion/queue-manager.h
new file mode 100644
index 0000000..aa5d31f
--- /dev/null
+++ b/drivers/spi/companion/queue-manager.h
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion queue management code
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#ifndef _BOSCH_COMPANION_QUEUE_MANAGEMENT_H
+#define _BOSCH_COMPANION_QUEUE_MANAGEMENT_H
+
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include "protocol.h"
+
+#define QUEUE_SIZE 16u
+
+/**
+ * struct companion_queue - encapsulate kfifo as companion queue
+ * @fifo: the kfifo object for companion packets
+ */
+struct companion_queue {
+ DECLARE_KFIFO(fifo, struct companion_packet, QUEUE_SIZE);
+};
+
+/**
+ * struct companion_queue_manager - manage all queues for companion
+ * @io_txq: the tx queue for IO messages
+ * @io_rxq: the rx queue for IO messages
+ * @can_txq: the tx queues for CAN messages
+ * @can_rxq: the rx queues for CAN messages
+ * @can_err: the queues for CAN error messages
+ * @can_txq_lock: lock for protect CAN tx queue 0
+ * @io_promoted: flag to indicate promoted IO messages priority
+ * @can_current: the currently scheduled CAN port
+ * @can_sched: counter of how many times CAN messages be scheduled
+ */
+struct companion_queue_manager {
+ struct companion_queue io_txq;
+ struct companion_queue io_rxq;
+ struct companion_queue can_txq[BCP_CAN_PORTS][BCP_CAN_PRIOS];
+ struct companion_queue can_rxq[BCP_CAN_PORTS];
+ struct companion_queue can_err[BCP_CAN_PORTS];
+ struct mutex can_txq_lock[BCP_CAN_PORTS];
+
+ bool io_promoted;
+ u8 can_current;
+ u32 can_sched;
+};
+
+/**
+ * qm_init() - initialize all managed queues
+ * @qm: address of the queue manager to be initialized
+ */
+void qm_init(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset() - reset all managed queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset_io() - reset managed IO queues
+ * @qm: address of the queue manager to be reset
+ */
+void qm_reset_io(struct companion_queue_manager *qm);
+
+/**
+ * qm_reset_can() - reset managed CAN queues
+ * @qm: address of the queue manager to be reset
+ * @port: port number of which CAN queue should be reset
+ */
+void qm_reset_can(struct companion_queue_manager *qm, u8 port);
+
+/**
+ * qm_has_tx_data() - return true if has tx data
+ * @qm: address of the queue manager to be used
+ */
+bool qm_has_tx_data(struct companion_queue_manager *qm);
+
+/**
+ * qm_get_tx_data() - return true if got the tx data
+ * @qm: address of the queue manager to be used
+ * @p: where the data to be copied
+ */
+bool qm_get_tx_data(struct companion_queue_manager *qm,
+ struct companion_packet *p);
+
+/**
+ * qm_io_txq_is_full() - return true if IO tx queue is full
+ * @qm: address of the queue manager to be used
+ */
+static inline bool qm_io_txq_is_full(struct companion_queue_manager *qm)
+{
+ return kfifo_is_full(&qm->io_txq.fifo);
+}
+
+/**
+ * qm_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @qm: address of the queue manager to be used
+ */
+static inline bool qm_io_rxq_is_empty(struct companion_queue_manager *qm)
+{
+ return kfifo_is_empty(&qm->io_rxq.fifo);
+}
+
+/**
+ * qm_io_txq_in() - put data from user sapce into IO tx queue
+ * @qm: address of the queue manager to be used
+ * @buf: address of the data to be put
+ * @count: number of bytes to be put
+ * @copied: address to store the number of copied bytes
+ */
+static inline int qm_io_txq_in(struct companion_queue_manager *qm,
+ const char __user *buf,
+ size_t count,
+ unsigned int *copied)
+{
+ return kfifo_from_user(&qm->io_txq.fifo, buf, count, copied);
+}
+
+/**
+ * qm_io_txq_out() - get data from the IO tx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be copied
+ */
+static inline bool qm_io_txq_out(struct companion_queue_manager *qm,
+ struct companion_packet *p)
+{
+ return kfifo_out(&qm->io_txq.fifo, p, 1) == 1;
+}
+
+/**
+ * qm_io_rxq_in() - put data into IO rx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be put
+ */
+static inline bool qm_io_rxq_in(struct companion_queue_manager *qm,
+ const struct companion_packet *p)
+{
+ return kfifo_in(&qm->io_rxq.fifo, p, 1) == 1;
+}
+
+/**
+ * qm_io_rxq_out() - copy data from the IO rx queue into user space
+ * @qm: address of the queue manager to be used
+ * @buf: address of the data to be copied
+ * @count: number of the bytes to be copied
+ * @copied: address to store the number of copied bytes
+ */
+static inline int qm_io_rxq_out(struct companion_queue_manager *qm,
+ char __user *buf,
+ size_t count,
+ unsigned int *copied)
+{
+ return kfifo_to_user(&qm->io_rxq.fifo, buf, count, copied);
+}
+
+/**
+ * qm_can_txq_in() - put data into CAN tx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be put
+ * @port: port number of which CAN queue array to be put
+ * @prio: priority of which CAN queue to be put
+ */
+static inline bool qm_can_txq_in(struct companion_queue_manager *qm,
+ const struct companion_packet *p,
+ u8 port,
+ u8 prio)
+{
+ bool result = false;
+
+ if (prio > 0)
+ return kfifo_in(&qm->can_txq[port][prio].fifo, p, 1) == 1;
+
+ /* queue 0 has multiple writers due to it sends both data and
+ * administrative frames, while queue 1-7 only send data frame
+ * (single writer), hence only queue 0 needs lock.
+ */
+ mutex_lock(&qm->can_txq_lock[port]);
+ result = (kfifo_in(&qm->can_txq[port][prio].fifo, p, 1) == 1);
+ mutex_unlock(&qm->can_txq_lock[port]);
+ return result;
+}
+
+/**
+ * qm_can_txq_out() - get data from the CAN tx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be copied
+ * @port: port number of which CAN queue array to be copied
+ * @prio: priority of which CAN queue to be copied
+ */
+static inline bool qm_can_txq_out(struct companion_queue_manager *qm,
+ struct companion_packet *p,
+ u8 port,
+ u8 prio)
+{
+ return kfifo_out(&qm->can_txq[port][prio].fifo, p, 1) == 1;
+}
+
+/**
+ * qm_can_rxq_in() - put data into CAN rx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be put
+ * @port: port number of which CAN queue to be put
+ */
+static inline bool qm_can_rxq_in(struct companion_queue_manager *qm,
+ const struct companion_packet *p,
+ u8 port)
+{
+ return kfifo_in(&qm->can_rxq[port].fifo, p, 1) == 1;
+}
+
+/**
+ * qm_can_rxq_out() - get data from the CAN rx queue
+ * @qm: address of the queue manager to be used
+ * @p: address of the data to be copied
+ * @port: port number of which CAN queue to be copied
+ */
+static inline bool qm_can_rxq_out(struct companion_queue_manager *qm,
+ struct companion_packet *p,
+ u8 port)
+{
+ return kfifo_out(&qm->can_rxq[port].fifo, p, 1) == 1;
+}
+
+static inline bool qm_can_err_in(struct companion_queue_manager *qm,
+ const struct companion_packet *p,
+ u8 port)
+{
+ return kfifo_in(&qm->can_err[port].fifo, p, 1) == 1;
+}
+
+static inline bool qm_can_err_out(struct companion_queue_manager *qm,
+ struct companion_packet *p,
+ u8 port)
+{
+ return kfifo_out(&qm->can_err[port].fifo, p, 1) == 1;
+}
+
+#endif
diff --git a/include/linux/companion.h b/include/linux/companion.h
new file mode 100644
index 0000000..fdc6258
--- /dev/null
+++ b/include/linux/companion.h
@@ -0,0 +1,259 @@
+/*
+ * Companion low level driver interface
+ *
+ * Copyright (C) 2017 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#ifndef _BOSCH_COMPANION_H
+#define _BOSCH_COMPANION_H
+
+#include <linux/can/dev.h>
+#include <linux/device.h>
+
+#define COMPANION_PACKET_SIZE 16
+
+/**
+ * struct companion_io_ops - callbacks of companion IO packets handling
+ * @on_tx_done: called when IO packets tx is done
+ * @on_rx_done: called when IO packets rx is done
+ */
+struct companion_io_ops {
+ void (*on_tx_done)(void *data);
+ void (*on_rx_done)(void *data);
+};
+
+/**
+ * struct companion_can_ops - callbacks of companion CAN packets handling
+ * @on_tx_done: called when CAN packets tx is done
+ * @on_rx_done: called when CAN packets rx is done
+ * @on_error: called when CAN error detected
+ * @on_tx_timeout: called when CAN packets tx timeout
+ */
+struct companion_can_ops {
+ void (*on_tx_done)(void *data, u8 prio, bool lost_seq, bool success);
+ void (*on_rx_done)(void *data);
+ void (*on_error)(void *data);
+ void (*on_tx_timeout)(void *data, u8 prio);
+};
+
+/**
+ * companion_io_ops_register() - register companion IO packets handler
+ * @parent: address of the caller parent device to be registered
+ * @ops: address of the IO packets callback
+ * @data: address of the IO packets callback argument
+ */
+int companion_io_ops_register(struct device *parent,
+ struct companion_io_ops *ops,
+ void *data);
+
+/**
+ * companion_io_ops_unregister() - unregister companion IO packets handler
+ * @parent: address of the caller parent device to be unregistered
+ */
+int companion_io_ops_unregister(struct device *parent);
+
+/**
+ * companion_can_ops_register() - register companion CAN packets handler
+ * @parent: address of the caller parent device to be registered
+ * @ops: address of the CAN packets callback
+ * @data: address of the CAN packets callback argument
+ */
+int companion_can_ops_register(struct device *parent,
+ u8 port,
+ struct companion_can_ops *ops,
+ void *data);
+
+/**
+ * companion_can_ops_unregister() - unregister comapnion CAN packets handler
+ * @parent: address of the caller parent device to be unregistered
+ * @port: port number of which CAN to be unregistered
+ */
+int companion_can_ops_unregister(struct device *parent, u8 port);
+
+/**
+ * companion_io_txq_is_full() - return true if IO tx queue is full
+ * @parent: address of the caller parent device to be used
+ */
+bool companion_io_txq_is_full(struct device *parent);
+
+/**
+ * companion_io_rxq_is_empty() - return true if IO rx queue is empty
+ * @parent: address of the caller parent device to be used
+ */
+bool companion_io_rxq_is_empty(struct device *parent);
+
+/**
+ * companion_do_io_tx() - send IO packets from user space to companion
+ * @parent: address of the caller parent device to be used
+ * @buf: address of the user space data to be sent
+ * @count: number of bytes to be sent
+ */
+int companion_do_io_tx(struct device *parent,
+ const char __user *buf,
+ size_t count);
+
+/**
+ * companion_do_io_rx() - receive IO packets from companion to user space
+ * @parent: address of the caller parent device to be used
+ * @buf: address of the data to be copied
+ * @count: number of bytes to be copied
+ */
+int companion_do_io_rx(struct device *parent,
+ char __user *buf,
+ size_t count);
+
+/**
+ * companion_do_can_tx() - send CAN data to companion
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be sent
+ * @prio: priority of the raw CAN frame to be sent
+ * @cf: address of the raw CAN frame to be sent
+ */
+int companion_do_can_tx(struct device *parent,
+ u8 port,
+ u8 prio,
+ const struct can_frame *cf);
+
+/**
+ * companion_do_can_rx() - receive CAN data from companion
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be received
+ * @cf: address of the raw CAN frame to be copied
+ */
+int companion_do_can_rx(struct device *parent,
+ u8 port,
+ struct can_frame *cf);
+
+/**
+ * companion_do_can_err() - receive CAN error from companion
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be received
+ * @bec: address of the error counter to be copied
+ * @state: address of the error state to be copied
+ * @code: address of the error code to be copied
+ */
+int companion_do_can_err(struct device *parent,
+ u8 port,
+ struct can_berr_counter *bec,
+ u8 *state,
+ u8 *code);
+
+#define COMPANION_CAN_STATE_WARNING BIT(0)
+#define COMPANION_CAN_STATE_PASSIVE BIT(1)
+#define COMPANION_CAN_STATE_BUS_OFF BIT(2)
+#define COMPANION_CAN_ERROR_STUFF BIT(0)
+#define COMPANION_CAN_ERROR_FORM BIT(1)
+#define COMPANION_CAN_ERROR_ACK BIT(2)
+#define COMPANION_CAN_ERROR_BIT1 BIT(3)
+#define COMPANION_CAN_ERROR_BIT0 BIT(4)
+#define COMPANION_CAN_ERROR_CRC BIT(5)
+#define COMPANION_CAN_ERROR_RXOV BIT(7)
+
+/**
+ * companion_do_set_can_bittiming() - set companion CAN bittiming
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be set
+ * @bittiming: the bittiming to be set
+ */
+int companion_do_set_can_bittiming(struct device *parent,
+ u8 port,
+ const struct can_bittiming *bittiming);
+
+/**
+ * companion_do_set_can_mode() - set companion CAN mode
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be set
+ * @mode: the mode to be set
+ */
+int companion_do_set_can_mode(struct device *parent,
+ u8 port,
+ enum can_mode mode);
+
+/**
+ * companion_do_set_can_ctrlmode() - set companion CAN control mode
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be set
+ * @ctrl: the control mode to be set
+ */
+int companion_do_set_can_ctrlmode(struct device *parent,
+ u8 port,
+ u32 ctrl);
+
+/**
+ * companion_do_get_can_status() - get companion CAN status
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be inquiry
+ * @bec: address of the error counter to be copied
+ */
+int companion_do_get_can_status(struct device *parent,
+ u8 port,
+ struct can_berr_counter *bec);
+
+/**
+ * companion_do_get_can_txq_status() - get companion CAN tx queue status
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @lost_txq_sync: flag of the given CAN tx queue lost sync or not
+ */
+int companion_do_get_can_txq_status(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *lost_txq_sync);
+
+/**
+ * companion_do_get_can_txq_status_all() - get all companion CAN tx queue status
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be inquiry
+ */
+int companion_do_get_can_txq_status_all(struct device *parent,
+ u8 port);
+
+/**
+ * companion_do_can_txq_is_full() - inquiry companion CAN tx queue is full
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @is_full: address of the is full result to be copied
+ */
+int companion_do_can_txq_is_full(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *is_full);
+
+/**
+ * companion_do_can_txq_has_space() - inquiry companion CAN tx queue has space
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be inquiry
+ * @prio: queue number of which tx queue to be inquiry
+ * @has_space: address of the has_space result to be copied
+ */
+int companion_do_can_txq_has_space(struct device *parent,
+ u8 port,
+ u8 prio,
+ bool *has_space);
+
+/**
+ * companion_do_can_start_tx_timer() - start companioin CAN tx timeout detection
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int companion_do_can_start_tx_timer(struct device *parent,
+ u8 port,
+ u8 prio);
+
+/**
+ * companion_do_can_stop_tx_timer() - stop companion CAN tx timeout detection
+ * @parent: address of the caller parent device to be used
+ * @port: port number of which CAN to be used
+ * @prio: queue number of which tx queue's timer to be used
+ */
+int companion_do_can_stop_tx_timer(struct device *parent,
+ u8 port,
+ u8 prio);
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v2 3/5] char: implement companion-char driver
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>
From: Zhu Yi <yi.zhu5@cn.bosch.com>
The upper level companion-char driver provides character device
interface to userspace for communicate IO messages with the
companion processor.
Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 3 +
drivers/char/companion.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 370 insertions(+)
create mode 100644 drivers/char/companion.c
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index c28dca0..e878d56 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -588,5 +588,12 @@ config TILE_SROM
source "drivers/char/xillybus/Kconfig"
+config COMPANION_CHAR
+ tristate "Character device for companion communication (Bosch)"
+ depends on COMPANION_SPI
+ help
+ The character device allows the userspace to exchange IO messages
+ with the Bosch companion processor via the companion SPI driver.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7dc3abe..0dae572 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -60,3 +60,6 @@ js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
+
+obj-$(CONFIG_COMPANION_CHAR) += companion-char.o
+companion-char-objs := companion.o
diff --git a/drivers/char/companion.c b/drivers/char/companion.c
new file mode 100644
index 0000000..2e26f01
--- /dev/null
+++ b/drivers/char/companion.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Companion upper level character device
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/companion.h>
+
+#define DRIVER_NAME "companion-char"
+
+static struct class *companion_char_class;
+static dev_t devt;
+
+/**
+ * struct companion_char_minor - companion-char minor structure
+ * @dev: address of the associated device
+ * @writelock: mutex to protect write
+ * @readlock: mutex to protect read
+ * @writewait: wait queue head of write
+ * @readwait: wait queue head of read
+ */
+struct companion_char_minor {
+ struct device *dev;
+ struct mutex writelock;
+ struct mutex readlock;
+ wait_queue_head_t writewait;
+ wait_queue_head_t readwait;
+};
+
+/**
+ * struct companion_char_priv - companion-char private data structure
+ * @cdev: char device
+ * @parent: address of the associated parent device
+ * @minors: address of the companion-char minor
+ */
+struct companion_char_priv {
+ struct cdev cdev;
+ struct device *parent;
+ struct companion_char_minor *minors;
+};
+
+/**
+ * companion_char_read() - read callback
+ * @filp: address of the associated virtual file
+ * @buf: address of the user space buffer to receive
+ * @count: number of bytes to read
+ * @offset: address of the read offset
+ */
+static ssize_t companion_char_read(struct file *filp,
+ char __user *buf,
+ size_t count,
+ loff_t *offset)
+{
+ unsigned int number = MINOR(file_inode(filp)->i_rdev);
+ struct companion_char_priv *priv = filp->private_data;
+ struct companion_char_minor *minor = &priv->minors[number];
+ int err;
+
+ if (count != COMPANION_PACKET_SIZE)
+ return -EMSGSIZE;
+
+ if (mutex_lock_interruptible(&minor->readlock))
+ return -ERESTARTSYS;
+
+ while (companion_io_rxq_is_empty(priv->parent)) {
+ mutex_unlock(&minor->readlock);
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (wait_event_interruptible(minor->readwait,
+ !companion_io_rxq_is_empty(priv->parent)))
+ return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&minor->readlock))
+ return -ERESTARTSYS;
+ }
+
+ err = companion_do_io_rx(priv->parent, buf, count);
+ mutex_unlock(&minor->readlock);
+ return err;
+}
+
+/**
+ * companion_char_write() - write callback
+ * @filp: address of the associated virtual file
+ * @buf: address of the user space buffer to transfer
+ * @count: number of bytes to write
+ * @offset: address of the write offset
+ */
+static ssize_t companion_char_write(struct file *filp,
+ const char __user *buf,
+ size_t count,
+ loff_t *offset)
+{
+ unsigned int number = MINOR(file_inode(filp)->i_rdev);
+ struct companion_char_priv *priv = filp->private_data;
+ struct companion_char_minor *minor = &priv->minors[number];
+ int err;
+
+ if (count != COMPANION_PACKET_SIZE)
+ return -EMSGSIZE;
+
+ if (mutex_lock_interruptible(&minor->writelock))
+ return -ERESTARTSYS;
+
+ while (companion_io_txq_is_full(priv->parent)) {
+ mutex_unlock(&minor->writelock);
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (wait_event_interruptible(minor->writewait,
+ !companion_io_txq_is_full(priv->parent)))
+ return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&minor->writelock))
+ return -ERESTARTSYS;
+ }
+
+ err = companion_do_io_tx(priv->parent, buf, count);
+ mutex_unlock(&minor->writelock);
+ return err;
+}
+
+/**
+ * companion_char_poll() - poll callback
+ * @filp: address of the associated virtual file
+ * @wait: address of the associated poll table
+ */
+static unsigned int companion_char_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int number = MINOR(file_inode(filp)->i_rdev);
+ struct companion_char_priv *priv = filp->private_data;
+ struct companion_char_minor *minor = &priv->minors[number];
+ unsigned int mask = 0;
+
+ poll_wait(filp, &minor->writewait, wait);
+ poll_wait(filp, &minor->readwait, wait);
+
+ mutex_lock(&minor->writelock);
+ if (!companion_io_txq_is_full(priv->parent))
+ mask |= POLLOUT | POLLWRNORM;
+ mutex_unlock(&minor->writelock);
+
+ mutex_lock(&minor->readlock);
+ if (!companion_io_rxq_is_empty(priv->parent))
+ mask |= POLLIN | POLLRDNORM;
+ mutex_unlock(&minor->readlock);
+
+ return mask;
+}
+
+/**
+ * companion_char_open() - open callback
+ * @inode: address of the associated inode
+ * @filp: address of the associated virtual file
+ */
+static int companion_char_open(struct inode *inode, struct file *filp)
+{
+ struct companion_char_priv *priv = container_of(
+ inode->i_cdev,
+ struct companion_char_priv,
+ cdev);
+
+ filp->private_data = priv;
+ nonseekable_open(inode, filp);
+ return 0;
+}
+
+/**
+ * companion_char_release() - release callback
+ * @inode: address of the associated inode
+ * @filp: address of the associated virtual file
+ */
+static int companion_char_release(struct inode *inode, struct file *filp)
+{
+ filp->private_data = NULL;
+ return 0;
+}
+
+static const struct file_operations companion_char_ops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = companion_char_read,
+ .write = companion_char_write,
+ .poll = companion_char_poll,
+ .open = companion_char_open,
+ .release = companion_char_release,
+};
+
+/**
+ * companion_char_on_tx_done() - tx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_char_on_tx_done(void *data)
+{
+ struct companion_char_priv *priv = data;
+ struct companion_char_minor *minor = &priv->minors[0];
+
+ wake_up_interruptible(&minor->writewait);
+}
+
+/**
+ * companion_char_on_rx_done() - rx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_char_on_rx_done(void *data)
+{
+ struct companion_char_priv *priv = data;
+ struct companion_char_minor *minor = &priv->minors[0];
+
+ wake_up_interruptible(&minor->readwait);
+}
+
+static struct companion_io_ops companion_char_io_ops = {
+ .on_tx_done = companion_char_on_tx_done,
+ .on_rx_done = companion_char_on_rx_done,
+};
+
+/**
+ * companion_char_probe() - probe callback
+ * @pdev: address of the platform device
+ */
+static int companion_char_probe(struct platform_device *pdev)
+{
+ struct companion_char_priv *priv;
+ struct companion_char_minor *minors;
+ int err;
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device found\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->parent = pdev->dev.parent;
+
+ minors = devm_kzalloc(&pdev->dev, sizeof(*minors), GFP_KERNEL);
+ if (!minors)
+ return -ENOMEM;
+
+ minors->dev = device_create(companion_char_class,
+ &pdev->dev,
+ MKDEV(MAJOR(devt), 0),
+ priv,
+ "companion%d",
+ 0);
+ if (IS_ERR_OR_NULL(minors->dev))
+ return PTR_ERR_OR_ZERO(minors->dev);
+ priv->minors = minors;
+
+ mutex_init(&minors->writelock);
+ mutex_init(&minors->readlock);
+ init_waitqueue_head(&minors->writewait);
+ init_waitqueue_head(&minors->readwait);
+
+ cdev_init(&priv->cdev, &companion_char_ops);
+ err = cdev_add(&priv->cdev, MKDEV(MAJOR(devt), 0), 1);
+ if (err) {
+ dev_err(&pdev->dev, "cdev_add() failed: %d\n", err);
+ goto on_error;
+ }
+
+ dev_set_drvdata(&pdev->dev, priv);
+
+ err = companion_io_ops_register(priv->parent,
+ &companion_char_io_ops,
+ priv);
+ if (err) {
+ dev_err(&pdev->dev, "companion_io_ops_register() failed: %d\n",
+ err);
+ goto on_error;
+ }
+ return 0;
+
+on_error:
+ device_destroy(companion_char_class, MKDEV(MAJOR(devt), 0));
+ cdev_del(&priv->cdev);
+ return err;
+}
+
+/**
+ * companion_char_remove() - remove callback
+ * @pdev: address of the platform device
+ */
+static int companion_char_remove(struct platform_device *pdev)
+{
+ struct companion_char_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ companion_io_ops_unregister(priv->parent);
+ device_destroy(companion_char_class, MKDEV(MAJOR(devt), 0));
+ cdev_del(&priv->cdev);
+ return 0;
+}
+
+static const struct of_device_id companion_char_of_match[] = {
+ { .compatible = "bosch,companion-char", .data = NULL, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_char_of_match);
+
+static struct platform_driver companion_char_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(companion_char_of_match),
+ },
+ .probe = companion_char_probe,
+ .remove = companion_char_remove,
+};
+
+/**
+ * companion_char_init() - module init
+ */
+static int __init companion_char_init(void)
+{
+ int err;
+
+ companion_char_class = class_create(THIS_MODULE, DRIVER_NAME);
+ if (IS_ERR_OR_NULL(companion_char_class))
+ return PTR_ERR_OR_ZERO(companion_char_class);
+
+ err = alloc_chrdev_region(&devt, 0, 1, DRIVER_NAME);
+ if (err) {
+ class_destroy(companion_char_class);
+ return err;
+ }
+
+ err = platform_driver_register(&companion_char_driver);
+ if (err) {
+ class_destroy(companion_char_class);
+ unregister_chrdev_region(devt, 1);
+ }
+
+ return err;
+}
+
+/**
+ * companion_char_exit() - module exit
+ */
+static void __exit companion_char_exit(void)
+{
+ platform_driver_unregister(&companion_char_driver);
+ class_destroy(companion_char_class);
+ unregister_chrdev_region(devt, 1);
+}
+
+module_init(companion_char_init);
+module_exit(companion_char_exit);
+
+MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>");
+MODULE_DESCRIPTION("Companion upper level character device");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v2 4/5] can: implement companion-can driver
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>
From: Zhu Yi <yi.zhu5@cn.bosch.com>
The upper level companion-can driver provides SocketCAN interface to
userspace for communicate CAN messages with the companion processor.
Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
drivers/net/can/Kconfig | 8 +
drivers/net/can/Makefile | 2 +
drivers/net/can/companion.c | 693 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 703 insertions(+)
create mode 100644 drivers/net/can/companion.c
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index ac4ff39..e403a7e 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -155,6 +155,14 @@ config PCH_CAN
is an IOH for x86 embedded processor (Intel Atom E6xx series).
This driver can access CAN bus.
+config COMPANION_CAN
+ tristate "Network device for companion communication (Bosch)"
+ depends on COMPANION_SPI
+ ---help---
+ The network device allows the userspace to use SocketCAN interface
+ to communicate with the Bosch companion processor via the companion
+ SPI driver.
+
source "drivers/net/can/c_can/Kconfig"
source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ifi_canfd/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 02b8ed7..575ea63 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -34,5 +34,7 @@ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
obj-$(CONFIG_PCH_CAN) += pch_can.o
+obj-$(CONFIG_COMPANION_CAN) += companion-can.o
+companion-can-objs := companion.o
subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
diff --git a/drivers/net/can/companion.c b/drivers/net/can/companion.c
new file mode 100644
index 0000000..0702d24
--- /dev/null
+++ b/drivers/net/can/companion.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Companion upper level can network device
+ *
+ * Copyright (C) 2015-2018 Bosch Sicherheitssysteme GmbH
+ *
+ * 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.
+ */
+
+#include <linux/can/dev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/companion.h>
+
+#define TX_QUEUE_DEPTH 16
+#define NUM_TX_QUEUES 8
+#define NUM_RX_QUEUES 1
+#define TX_ECHO_SKB_MAX (NUM_TX_QUEUES * TX_QUEUE_DEPTH)
+#define DRIVER_NAME "companion-can"
+
+/**
+ * struct companion_can_priv - companion-can private data structure
+ * @can: standard common CAN private data, must be first member
+ * @parent: address of the associated parent device
+ * @dev: address of the associated network device
+ * @port: the companion CAN port number
+ * @tx_head: array of all tx queue head
+ * @tx_tail: arrat of all tx queue tail
+ */
+struct companion_can_priv {
+ struct can_priv can;
+ struct device *parent;
+ struct net_device *dev;
+ u8 port;
+ u8 tx_head[NUM_TX_QUEUES];
+ u8 tx_tail[NUM_TX_QUEUES];
+};
+
+/**
+ * companion_can_put_echo_skb() - put echo skb into ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to put
+ * @skb: address of the packet to put
+ */
+static void companion_can_put_echo_skb(struct companion_can_priv *priv,
+ u8 prio,
+ struct sk_buff *skb)
+{
+ u8 offset = prio * TX_QUEUE_DEPTH;
+ u8 index = priv->tx_head[prio] % TX_QUEUE_DEPTH;
+
+ can_put_echo_skb(skb, priv->dev, offset + index);
+ priv->tx_head[prio]++;
+}
+
+/**
+ * companion_can_get_echo_skb() - get echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to get
+ */
+static u8 companion_can_get_echo_skb(struct companion_can_priv *priv, u8 prio)
+{
+ u8 offset, index, result = 0;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ result = can_get_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+ return result;
+}
+
+/**
+ * companion_can_free_echo_skb() - free echo skb from ring buffer
+ * @priv: address of companion-can private data
+ * @prio: which CAN queue to free
+ */
+static void companion_can_free_echo_skb(struct companion_can_priv *priv,
+ u8 prio)
+{
+ u8 offset, index;
+
+ if (priv->tx_head[prio] != priv->tx_tail[prio]) {
+ offset = prio * TX_QUEUE_DEPTH;
+ index = priv->tx_tail[prio] % TX_QUEUE_DEPTH;
+ can_free_echo_skb(priv->dev, offset + index);
+ priv->tx_tail[prio]++;
+ }
+}
+
+/**
+ * companion_can_set_bittiming() - set CAN bittiming
+ * @dev: address of the associated network device
+ */
+static int companion_can_set_bittiming(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ const struct can_bittiming *bt = &priv->can.bittiming;
+ u32 ctrl = priv->can.ctrlmode;
+ int err;
+
+ err = companion_do_set_can_bittiming(priv->parent, priv->port, bt);
+ if (err)
+ return err;
+
+ if (ctrl & CAN_CTRLMODE_LISTENONLY) {
+ err = companion_do_set_can_ctrlmode(priv->parent,
+ priv->port,
+ ctrl);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_set_mode() - set CAN mode
+ * @dev: address of the associated network device
+ * @mode: the CAN mode to set
+ */
+static int companion_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = companion_can_set_bittiming(dev);
+ if (err)
+ return err;
+ /* fall through */
+
+ case CAN_MODE_STOP:
+ err = companion_do_set_can_mode(priv->parent,
+ priv->port,
+ mode);
+ if (err)
+ return err;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * companion_can_get_berr_counter() - get CAN error counter
+ * @dev: address of the associated network device
+ * @bec: address of the CAN error counter to store
+ */
+static int companion_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+
+ return companion_do_get_can_status(priv->parent, priv->port, bec);
+}
+
+/**
+ * companion_can_handle_state() - handle CAN state transition
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN state
+ * @bec: address of the CAN error counter
+ * @state: the companion CAN state
+ */
+static void companion_can_handle_state(struct net_device *dev,
+ struct can_frame *cf,
+ struct can_berr_counter *bec,
+ u8 state)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state rx_state = CAN_STATE_ERROR_ACTIVE;
+ enum can_state tx_state = CAN_STATE_ERROR_ACTIVE;
+
+ if (state & COMPANION_CAN_STATE_BUS_OFF) {
+ new_state = CAN_STATE_BUS_OFF;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_PASSIVE) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ rx_state = bec->rxerr > 127 ? new_state : rx_state;
+ tx_state = bec->txerr > 127 ? new_state : tx_state;
+ } else if (state & COMPANION_CAN_STATE_WARNING) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ rx_state = bec->rxerr >= bec->txerr ? new_state : rx_state;
+ tx_state = bec->txerr >= bec->rxerr ? new_state : tx_state;
+ }
+
+ if (new_state != priv->can.state) {
+ can_change_state(dev, cf, tx_state, rx_state);
+
+ if (new_state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ }
+}
+
+/**
+ * companion_can_handle_error() - handle CAN error
+ * @dev: address of the associated network device
+ * @cf: address of the CAN frame to store CAN error
+ * @code: the companion CAN error code
+ */
+static void companion_can_handle_error(struct net_device *dev,
+ struct can_frame *cf,
+ u8 code)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+
+ if (code & COMPANION_CAN_ERROR_RXOV) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ dev->stats.rx_over_errors++;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & (COMPANION_CAN_ERROR_STUFF |
+ COMPANION_CAN_ERROR_FORM |
+ COMPANION_CAN_ERROR_ACK |
+ COMPANION_CAN_ERROR_BIT1 |
+ COMPANION_CAN_ERROR_BIT0 |
+ COMPANION_CAN_ERROR_CRC)) {
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if (code & COMPANION_CAN_ERROR_STUFF) {
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_FORM) {
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ dev->stats.rx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_ACK) {
+ cf->can_id |= CAN_ERR_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT1) {
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_BIT0) {
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ dev->stats.tx_errors++;
+ }
+
+ if (code & COMPANION_CAN_ERROR_CRC) {
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ dev->stats.rx_errors++;
+ }
+
+ priv->can.can_stats.bus_error++;
+ }
+}
+
+/**
+ * companion_can_poll_err() - poll CAN error packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_err(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_berr_counter bec;
+ u8 state;
+ u8 code;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ if (companion_do_can_err(priv->parent,
+ priv->port,
+ &bec,
+ &state,
+ &code) != 0)
+ return false;
+
+ skb = alloc_can_err_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc err skb\n");
+ return false;
+ }
+
+ companion_can_handle_state(dev, cf, &bec, state);
+ companion_can_handle_error(dev, cf, code);
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ return true;
+}
+
+/**
+ * companion_can_poll_data() - poll CAN data packet from companion
+ * @dev: address of the associated network device
+ */
+static bool companion_can_poll_data(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct can_frame *cf;
+
+ skb = alloc_can_skb(dev, &cf);
+ if (!skb) {
+ dev_err(&dev->dev, "cannot alloc rx skb\n");
+ dev->stats.rx_dropped++;
+ return false;
+ }
+
+ if (companion_do_can_rx(priv->parent, priv->port, cf) != 0) {
+ dev_kfree_skb_any(skb);
+ return false;
+ }
+
+ dev->stats.rx_bytes += cf->can_dlc;
+ dev->stats.rx_packets++;
+ netif_rx(skb);
+ can_led_event(dev, CAN_LED_EVENT_RX);
+ return true;
+}
+
+/**
+ * companion_can_on_tx_done() - CAN tx done callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue is done
+ * @lost_seq_sync: flag indicate lost sequence happened
+ * @success: flag indicate last send is succeed or not
+ */
+static void companion_can_on_tx_done(void *data,
+ u8 prio,
+ bool lost_seq_sync,
+ bool success)
+{
+ struct companion_can_priv *priv = data;
+ struct net_device *dev = priv->dev;
+ struct net_device_stats *stats = &dev->stats;
+ int err;
+
+ if (success) {
+ stats->tx_bytes += companion_can_get_echo_skb(priv, prio);
+ stats->tx_packets++;
+ can_led_event(dev, CAN_LED_EVENT_TX);
+ } else {
+ companion_can_free_echo_skb(priv, prio);
+ dev_err(&dev->dev, "on_tx_done(%d) failed\n", prio);
+ }
+
+ if (lost_seq_sync)
+ dev_err(&dev->dev, "txq[%d] lost sequence sync\n", prio);
+
+ err = companion_do_can_stop_tx_timer(priv->parent, priv->port, prio);
+ if (err)
+ dev_err(&dev->dev,
+ "stop txq[%d] tx timer failed: %d\n",
+ prio, err);
+
+ netif_wake_subqueue(dev, prio);
+}
+
+/**
+ * companion_can_on_rx_done() - CAN rx done callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_rx_done(void *data)
+{
+ struct companion_can_priv *priv = data;
+
+ while (companion_can_poll_data(priv->dev))
+ ;
+}
+
+/**
+ * companion_can_on_error() - CAN error callback
+ * @data: address of user supplied callback data
+ */
+static void companion_can_on_error(void *data)
+{
+ struct companion_can_priv *priv = data;
+
+ while (companion_can_poll_err(priv->dev))
+ ;
+}
+
+/**
+ * companion_can_on_tx_timeout() - CAN tx timeout callback
+ * @data: address of user supplied callback data
+ * @prio: which CAN queue tx timed out
+ */
+static void companion_can_on_tx_timeout(void *data, u8 prio)
+{
+ struct companion_can_priv *priv = data;
+ bool lost_txq_sync = false;
+ int err;
+
+ err = companion_do_get_can_txq_status(priv->parent,
+ priv->port,
+ prio,
+ &lost_txq_sync);
+ if (err) {
+ dev_err(&priv->dev->dev,
+ "get can txq[%d] status failed: %d\n", prio, err);
+
+ if (err != -EINVAL)
+ companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ return;
+ }
+
+ if (lost_txq_sync) {
+ dev_err(&priv->dev->dev,
+ "txq[%d] out of sync, restart data flow\n", prio);
+ companion_can_free_echo_skb(priv, prio);
+ netif_wake_subqueue(priv->dev, prio);
+ } else {
+ dev_err(&priv->dev->dev,
+ "txq[%d] is sync'd, but no ack, wait again\n", prio);
+ companion_do_can_start_tx_timer(priv->parent, priv->port, prio);
+ }
+}
+
+static struct companion_can_ops companion_can_can_ops = {
+ .on_tx_done = companion_can_on_tx_done,
+ .on_rx_done = companion_can_on_rx_done,
+ .on_error = companion_can_on_error,
+ .on_tx_timeout = companion_can_on_tx_timeout,
+};
+
+/**
+ * companion_can_open() - ndo_open callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_open(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ bool has_space = false;
+ int err, i;
+
+ err = companion_can_ops_register(priv->parent,
+ priv->port,
+ &companion_can_can_ops,
+ priv);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_ops_register() failed: %d\n", err);
+ goto out;
+ }
+
+ err = companion_can_set_mode(dev, CAN_MODE_START);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_can_set_mode() failed: %d\n", err);
+ goto out_register;
+ }
+
+ err = companion_do_get_can_txq_status_all(priv->parent, priv->port);
+ if (err) {
+ dev_err(&dev->dev,
+ "companion_do_get_can_txq_status_all() failed: %d\n",
+ err);
+ goto out_mode;
+ }
+
+ err = open_candev(dev);
+ if (err) {
+ dev_err(&dev->dev, "open_candev() failed: %d\n", err);
+ goto out_mode;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ can_led_event(dev, CAN_LED_EVENT_OPEN);
+
+ for (i = 0; i < NUM_TX_QUEUES; ++i) {
+ err = companion_do_can_txq_has_space(priv->parent,
+ priv->port,
+ i,
+ &has_space);
+
+ if (!err && has_space) {
+ netif_tx_start_queue(netdev_get_tx_queue(dev, i));
+ } else {
+ netif_tx_stop_queue(netdev_get_tx_queue(dev, i));
+ dev_err(&dev->dev, "txq[%d] is not started\n", i);
+ }
+ }
+
+ return 0;
+
+out_mode:
+ companion_can_set_mode(dev, CAN_MODE_STOP);
+out_register:
+ companion_can_ops_unregister(priv->parent, priv->port);
+out:
+ return err;
+}
+
+/**
+ * companion_can_release() - ndo_close callback
+ * @dev: address of the associated network device
+ */
+static int companion_can_release(struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ int err;
+
+ netif_tx_stop_all_queues(dev);
+ can_led_event(dev, CAN_LED_EVENT_STOP);
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(dev);
+ err = companion_can_set_mode(dev, CAN_MODE_STOP);
+ companion_can_ops_unregister(priv->parent, priv->port);
+ return err;
+}
+
+/**
+ * companion_can_start_xmit() - ndo_start_xmit callback
+ * @skb: address of the packet to send
+ * @dev: address of the associated network device
+ */
+static int companion_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct companion_can_priv *priv = netdev_priv(dev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ u16 prio = skb_get_queue_mapping(skb);
+ bool is_full = false;
+ int err;
+
+ if (can_dropped_invalid_skb(dev, skb)) {
+ dev_err(&dev->dev, "dropped invalid skb on txq[%d]\n", prio);
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_tx(priv->parent, priv->port, prio, cf);
+ if (err) {
+ dev_err(&dev->dev, "dropped packet on txq[%d]\n", prio);
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ err = companion_do_can_txq_is_full(priv->parent,
+ priv->port,
+ prio,
+ &is_full);
+ if (!err && is_full) {
+ netif_stop_subqueue(dev, prio);
+ err = companion_do_can_start_tx_timer(priv->parent,
+ priv->port,
+ prio);
+ if (err)
+ dev_err(&dev->dev,
+ "start txq[%d] tx timer failed: %d\n",
+ prio, err);
+ }
+
+ companion_can_put_echo_skb(priv, prio, skb);
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops companion_can_netdev_ops = {
+ .ndo_open = companion_can_open,
+ .ndo_stop = companion_can_release,
+ .ndo_start_xmit = companion_can_start_xmit,
+};
+
+static const struct of_device_id companion_can_of_match[] = {
+ { .compatible = "bosch,companion-can", .data = NULL, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, companion_can_of_match);
+
+static const struct can_bittiming_const companion_can_bittiming_const = {
+ .name = DRIVER_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 1024,
+ .brp_inc = 1,
+};
+
+/**
+ * companion_can_probe() - probe callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct net_device *dev;
+ struct companion_can_priv *priv;
+ u32 port, freq;
+ int err;
+
+ if (!node) {
+ dev_err(&pdev->dev, "no device tree data\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(node, "port", &port)) {
+ dev_err(&pdev->dev, "no port property\n");
+ return -ENODEV;
+ }
+
+ if (port != 0 && port != 1) {
+ dev_err(&pdev->dev,
+ "invalid port %d, valid range is [0,1]\n", port);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "clock-frequency", &freq)) {
+ dev_err(&pdev->dev, "no clock-frequency property\n");
+ return -ENODEV;
+ }
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device\n");
+ return -ENODEV;
+ }
+
+ dev = alloc_candev_mqs(sizeof(*priv),
+ TX_ECHO_SKB_MAX,
+ NUM_TX_QUEUES,
+ NUM_RX_QUEUES);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->netdev_ops = &companion_can_netdev_ops;
+ dev->flags |= IFF_ECHO;
+ dev->real_num_tx_queues = NUM_TX_QUEUES;
+
+ priv = netdev_priv(dev);
+ priv->can.clock.freq = freq;
+ priv->can.bittiming_const = &companion_can_bittiming_const;
+ priv->can.do_set_mode = companion_can_set_mode;
+ priv->can.do_get_berr_counter = companion_can_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING;
+ priv->parent = pdev->dev.parent;
+ priv->dev = dev;
+ priv->port = port;
+
+ platform_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_candev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "register_candev() failed: %d\n", err);
+ free_candev(dev);
+ return err;
+ }
+
+ devm_can_led_init(dev);
+ return 0;
+}
+
+/**
+ * companion_can_remove() - remove callback
+ * @pdev: address of the platform device
+ */
+static int companion_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_candev(dev);
+ free_candev(dev);
+ return 0;
+}
+
+static struct platform_driver companion_can_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(companion_can_of_match),
+ },
+ .probe = companion_can_probe,
+ .remove = companion_can_remove,
+};
+module_platform_driver(companion_can_driver);
+
+MODULE_AUTHOR("Zhu Yi <yi.zhu5@cn.bosch.com>");
+MODULE_DESCRIPTION("Companion upper level can network device");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v2 5/5] spi,can,char: add companion DT binding documentation
From: Mark Jonas @ 2018-06-13 14:37 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde
Cc: linux-can, netdev, linux-kernel, hs, yi.zhu5, petar.petrovic2,
stephan.baetge, andy.shevchenko, socketcan, o.rempel, Mark Jonas
In-Reply-To: <1528900641-18677-1-git-send-email-mark.jonas@de.bosch.com>
From: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
.../devicetree/bindings/spi/bosch,companion.txt | 82 ++++++++++++++++++++++
1 file changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/bosch,companion.txt
diff --git a/Documentation/devicetree/bindings/spi/bosch,companion.txt b/Documentation/devicetree/bindings/spi/bosch,companion.txt
new file mode 100644
index 0000000..5ded325
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/bosch,companion.txt
@@ -0,0 +1,82 @@
+Bosch Companion SPI slave device
+
+The functionality bases on an external peripheral chip named Companion.
+It offers two CAN interfaces, each has 8 prioritized transmit FIFOs as
+well as one receive FIFO. Besides CAN, undisclosed additional functions
+can be accessed through the char device.
+
+A standard SPI interface with two additional lines for flow control is
+used. The Companion chip is the SPI slave.
+
+The driver suite consists of three separate drivers. The following
+diagram illustrates the dependencies in layers.
+
+ /dev/companion SocketCAN User Space
+-------------------------------------------------------------------
+ +----------------+ +---------------+
+ | companion-char | | companion-can |
+ +----------------+ +---------------+
+ +----------------------------------+
+ | companion-spi |
+ +----------------------------------+
+ +----------------------------------+
+ | standard SPI subsystem |
+ +----------------------------------+ Linux Kernel
+-------------------------------------------------------------------
+ | | | | | | Hardware
+ CS-+ | | | | +-BUSY
+ CLK--+ | | +---REQUEST
+ MOSI---+ |
+ MISO-----+
+
+Required properties:
+
+- compatible : must be "bosch,companion-spi"
+- interrupt-parent : the phandle of the GPIO controller
+- interrupts : (GPIO) interrupt to which 'request-gpios' is
+ connected to
+- request-gpios : GPIO pin to request SPI master to receive data
+- busy-gpios : GPIO pin to indicate SPI slave is busy
+- cs-gpios : GPIO pin to select SPI slave
+
+Optional properties:
+
+The controller supports at most 2 CAN and 1 char device subnodes. When
+optionally specify the subnodes, the following properties are required:
+
+- CAN subnode
+ - compatible : must be "bosch,companion-can"
+ - clock-frequency: CAN device clock in Hz
+ - port : must be 0 or 1
+
+- Char device subnode
+ - compatible : must be "bosch,companion-char"
+
+Example:
+
+&ecspi1 {
+ companion-spi@0 {
+ compatible = "bosch,companion-spi";
+ interrupt-parent = <&gpio1>;
+ interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+ request-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>;
+ busy-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
+ cs-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
+
+ companion-can0 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <0>;
+ };
+
+ companion-can1 {
+ compatible = "bosch,companion-can";
+ clock-frequency = <28000000>;
+ port = <1>;
+ };
+
+ companion-char {
+ compatible = "bosch,companion-char";
+ };
+ };
+};
--
2.7.4
^ permalink raw reply related
* Re: [PATCH net 1/2] ipv4: igmp: use alarmtimer to prevent delayed reports
From: Andrew Lunn @ 2018-06-13 14:44 UTC (permalink / raw)
To: Tejaswi Tanikella; +Cc: netdev, f.fainelli, davem
In-Reply-To: <20180613133256.GA20232@tejaswit-linux.qualcomm.com>
> I'll try to explain my scenario. This was observed on a arm64 device.
> An application registers for a mcast group, and just listens to mcast
> packets. The connection is setup and mcast packets are being forwarded
> by the router. Multicast packets are sent out every few minutes.
> Not a very busy connection.
>
> After some time the router sends out a IGMPv2 query. The max delay in
> the header is set to 10s. The system starts a timer to send out the
> response at 9s. But the device suspends and wakes up after 60s.
>
> The response is sent out 50s late.
>
> ftrace logs with boottime trace_clock and my igmp_logs:
>
> 4740693 kworker/0:3-395 [000] ..s2 4716.425695: igmp_log: skbaddr=ffffffc156fe6600 daddr=224.0.0.1, id=0, rc=4295217721
> 4740694 kworker/0:3-395 [000] d.s4 4716.425717: timer_start:timer=ffffffc217763140 function=igmp_timer_expire expires=4295218678 [timeout=957] flags=0x00000000
> timer set for 9.57 seconds. 957 jiffies
>
> < device suspends >
>
> < wakes up after ~60s >
> 4781289 <idle>-0 [000] .ns2 4779.170886: timer_expire_entry: timer=ffffffc217763140 function=igmp_timer_expire now=4295218678
> 4781290 <idle>-0 [000] .ns2 4779.171045: igmp_log: skbaddr=ffffffc1559d0e00 daddr=227.226.228.225, id=0, rc=4295218678
>
> Since the response was delayed, mcast packets are not forwarded by the
> router.
While it has been asleep, it has also been dropping any multicast
traffic in the stream. So it does not really matter it has left the
group. You were not receiving the packets anyway.
Thing about this from another angle. I have an NTP client running on
my laptop, using multicast address 224.0.1.1. I suspend my laptop and
walk away for two hours. When i come back, i find that 20 seconds
after i suspended it, it resumed and send an group response
message. And an hour later, since it was still running, the battery
went flat.
It seems to me, the change you are proposing cannot be the default
behaviour.
I actually think you need to be looking at some sort of WoL feature.
You need the multicast stream data packets to wake you, and you also
need to wake up the IGMP query message. And you need to wake up to
send the group membership. Does your hardware have this sort of WoL
support? You can then explicitly enable this WoL for your application.
Andrew
^ permalink raw reply
* Re: [PATCH 1/1] ip: add rmnet initial support
From: Daniele Palmas @ 2018-06-13 14:53 UTC (permalink / raw)
To: Subash Abhinov Kasiviswanathan; +Cc: netdev, Stephen Hemminger
In-Reply-To: <8be93f95bedfdf6983b6af6894eecafa@codeaurora.org>
2018-06-13 1:06 GMT+02:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
>> +
>> +static void print_explain(FILE *f)
>> +{
>> + fprintf(f,
>> + "Usage: ... rmnet mux_id MUXID\n"
>> + "\n"
>> + "MUXID := 1-127\n"
>> + );
>> +}
>
>
> Hi Daniele
>
> This range can be from 1-254.
>
>> +
>> +static void explain(void)
>> +{
>> + print_explain(stderr);
>> +}
>> +
>> +static int rmnet_parse_opt(struct link_util *lu, int argc, char **argv,
>> + struct nlmsghdr *n)
>> +{
>> + __u16 mux_id;
>> +
>> + while (argc > 0) {
>> + if (matches(*argv, "mux_id") == 0) {
>> + NEXT_ARG();
>> + if (get_u16(&mux_id, *argv, 0))
>> + invarg("mux_id is invalid", *argv);
>> + addattr_l(n, 1024, IFLA_RMNET_MUX_ID, &mux_id, 2);
>
>
> You could use addattr16() instead since it is __u16.
>
Thanks Subash, I'll fix those in v2.
Regards,
Daniele
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH 1/1] ip: add rmnet initial support
From: Daniele Palmas @ 2018-06-13 14:54 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, Subash Abhinov Kasiviswanathan
In-Reply-To: <20180612172225.1c033767@xeon-e3>
2018-06-13 2:22 GMT+02:00 Stephen Hemminger <stephen@networkplumber.org>:
> On Tue, 12 Jun 2018 16:12:57 +0200
> Daniele Palmas <dnlplm@gmail.com> wrote:
>
>> This patch adds basic support for Qualcomm rmnet devices.
>>
>> Signed-off-by: Daniele Palmas <dnlplm@gmail.com>
>> ---
>> ip/Makefile | 2 +-
>> ip/iplink.c | 2 +-
>> ip/iplink_rmnet.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 72 insertions(+), 2 deletions(-)
>> create mode 100644 ip/iplink_rmnet.c
> };
>
> I am glad to see integrated tool support, but this needs to be targeted at
> the iproute2-next since it is a new feature.
>
> Some things that I would like to see changed:
> 1. All of iproute2 is now using SPDX license identifiers, you should not
> include GPL boilerplate
> 2. You should provide dump (print_opt) as well as parse routine.
> Output format should use the print_uint (json print) routines.
> 3. Please update manual page (man/man8/ip-link.8.in) to include the new
> option.
>
Thanks for the review Stephen, I'll submit a v2 for fixing those issues.
Regards,
Daniele
^ permalink raw reply
* (unknown),
From: Ubaithullah Masood @ 2018-06-13 15:48 UTC (permalink / raw)
Could you act as the beneficiary to claim 9.8M USD that my bank wants
to consifacte? I will give you 50% and Every documentation would be
put in placed.
Mr Ubaithullah from Hong Kong.
^ permalink raw reply
* RE: [PATCH 4/4] net: emaclite: Remove xemaclite_mdio_setup return check
From: Radhey Shyam Pandey @ 2018-06-13 16:02 UTC (permalink / raw)
To: Andrew Lunn
Cc: davem@davemloft.net, michal.simek@xilinx.com,
netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20180613072905.GE24314@lunn.ch>
> -----Original Message-----
> From: Andrew Lunn [mailto:andrew@lunn.ch]
> Sent: Wednesday, June 13, 2018 12:59 PM
> To: Radhey Shyam Pandey <radheys@xilinx.com>
> Cc: davem@davemloft.net; michal.simek@xilinx.com;
> netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org
> Subject: Re: [PATCH 4/4] net: emaclite: Remove xemaclite_mdio_setup return
> check
>
> On Wed, Jun 13, 2018 at 12:05:19PM +0530, Radhey Shyam Pandey wrote:
> > Errors are already reported in xemaclite_mdio_setup so avoid
> > reporting it again.
> >
> > Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> > Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> > ---
> > drivers/net/ethernet/xilinx/xilinx_emaclite.c | 4 +---
> > 1 files changed, 1 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > index ec4608e..2a0c06e 100644
> > --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > @@ -1143,9 +1143,7 @@ static int xemaclite_of_probe(struct
> platform_device *ofdev)
> > xemaclite_update_address(lp, ndev->dev_addr);
> >
> > lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-
> handle", 0);
> > - rc = xemaclite_mdio_setup(lp, &ofdev->dev);
> > - if (rc)
> > - dev_warn(&ofdev->dev, "error registering MDIO bus\n");
> > + xemaclite_mdio_setup(lp, &ofdev->dev);
> >
> > dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr);
>
> The patch itself is O.K.
>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
>
> However, do you want to keep going if the MDIO bus fails? Maybe you
> should failed the probe?
Thanks for the review. Yes, I will fix it in next series.
-Radhey
>
> Andrew
^ permalink raw reply
* [PATCH net] netfilter: nf_queue: augment nfqa_cfg_policy
From: Eric Dumazet @ 2018-06-13 16:13 UTC (permalink / raw)
To: Pablo Neira Ayuso, Jozsef Kadlecsik, Florian Westphal
Cc: netfilter-devel, netdev, Eric Dumazet, Eric Dumazet
Three attributes are currently not verified, thus can trigger KMSAN
warnings such as :
BUG: KMSAN: uninit-value in __arch_swab32 arch/x86/include/uapi/asm/swab.h:10 [inline]
BUG: KMSAN: uninit-value in __fswab32 include/uapi/linux/swab.h:59 [inline]
BUG: KMSAN: uninit-value in nfqnl_recv_config+0x939/0x17d0 net/netfilter/nfnetlink_queue.c:1268
CPU: 1 PID: 4521 Comm: syz-executor120 Not tainted 4.17.0+ #5
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x185/0x1d0 lib/dump_stack.c:113
kmsan_report+0x188/0x2a0 mm/kmsan/kmsan.c:1117
__msan_warning_32+0x70/0xc0 mm/kmsan/kmsan_instr.c:620
__arch_swab32 arch/x86/include/uapi/asm/swab.h:10 [inline]
__fswab32 include/uapi/linux/swab.h:59 [inline]
nfqnl_recv_config+0x939/0x17d0 net/netfilter/nfnetlink_queue.c:1268
nfnetlink_rcv_msg+0xb2e/0xc80 net/netfilter/nfnetlink.c:212
netlink_rcv_skb+0x37e/0x600 net/netlink/af_netlink.c:2448
nfnetlink_rcv+0x2fe/0x680 net/netfilter/nfnetlink.c:513
netlink_unicast_kernel net/netlink/af_netlink.c:1310 [inline]
netlink_unicast+0x1680/0x1750 net/netlink/af_netlink.c:1336
netlink_sendmsg+0x104f/0x1350 net/netlink/af_netlink.c:1901
sock_sendmsg_nosec net/socket.c:629 [inline]
sock_sendmsg net/socket.c:639 [inline]
___sys_sendmsg+0xec8/0x1320 net/socket.c:2117
__sys_sendmsg net/socket.c:2155 [inline]
__do_sys_sendmsg net/socket.c:2164 [inline]
__se_sys_sendmsg net/socket.c:2162 [inline]
__x64_sys_sendmsg+0x331/0x460 net/socket.c:2162
do_syscall_64+0x15b/0x230 arch/x86/entry/common.c:287
entry_SYSCALL_64_after_hwframe+0x44/0xa9
RIP: 0033:0x43fd59
RSP: 002b:00007ffde0e30d28 EFLAGS: 00000213 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00000000004002c8 RCX: 000000000043fd59
RDX: 0000000000000000 RSI: 0000000020000080 RDI: 0000000000000003
RBP: 00000000006ca018 R08: 00000000004002c8 R09: 00000000004002c8
R10: 00000000004002c8 R11: 0000000000000213 R12: 0000000000401680
R13: 0000000000401710 R14: 0000000000000000 R15: 0000000000000000
Uninit was created at:
kmsan_save_stack_with_flags mm/kmsan/kmsan.c:279 [inline]
kmsan_internal_poison_shadow+0xb8/0x1b0 mm/kmsan/kmsan.c:189
kmsan_kmalloc+0x94/0x100 mm/kmsan/kmsan.c:315
kmsan_slab_alloc+0x10/0x20 mm/kmsan/kmsan.c:322
slab_post_alloc_hook mm/slab.h:446 [inline]
slab_alloc_node mm/slub.c:2753 [inline]
__kmalloc_node_track_caller+0xb35/0x11b0 mm/slub.c:4395
__kmalloc_reserve net/core/skbuff.c:138 [inline]
__alloc_skb+0x2cb/0x9e0 net/core/skbuff.c:206
alloc_skb include/linux/skbuff.h:988 [inline]
netlink_alloc_large_skb net/netlink/af_netlink.c:1182 [inline]
netlink_sendmsg+0x76e/0x1350 net/netlink/af_netlink.c:1876
sock_sendmsg_nosec net/socket.c:629 [inline]
sock_sendmsg net/socket.c:639 [inline]
___sys_sendmsg+0xec8/0x1320 net/socket.c:2117
__sys_sendmsg net/socket.c:2155 [inline]
__do_sys_sendmsg net/socket.c:2164 [inline]
__se_sys_sendmsg net/socket.c:2162 [inline]
__x64_sys_sendmsg+0x331/0x460 net/socket.c:2162
do_syscall_64+0x15b/0x230 arch/x86/entry/common.c:287
entry_SYSCALL_64_after_hwframe+0x44/0xa9
Fixes: fdb694a01f1f ("netfilter: Add fail-open support")
Fixes: 829e17a1a602 ("[NETFILTER]: nfnetlink_queue: allow changing queue length through netlink")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
---
net/netfilter/nfnetlink_queue.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 4ccd2988f9db637166358335d8e26299c7237bec..ea4ba551abb28cb25c833dc408e23d1313b21bb4 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1243,6 +1243,9 @@ static int nfqnl_recv_unsupp(struct net *net, struct sock *ctnl,
static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = {
[NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) },
[NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) },
+ [NFQA_CFG_QUEUE_MAXLEN] = { .type = NLA_U32 },
+ [NFQA_CFG_MASK] = { .type = NLA_U32 },
+ [NFQA_CFG_FLAGS] = { .type = NLA_U32 },
};
static const struct nf_queue_handler nfqh = {
--
2.18.0.rc1.242.g61856ae69a-goog
^ permalink raw reply related
* KASAN: slab-out-of-bounds Read in bpf_skb_vlan_push
From: syzbot @ 2018-06-13 16:17 UTC (permalink / raw)
To: ast, daniel, davem, linux-kernel, netdev, syzkaller-bugs
Hello,
syzbot found the following crash on:
HEAD commit: 75d4e704fa8d netdev-FAQ: clarify DaveM's position for stab..
git tree: bpf-next
console output: https://syzkaller.appspot.com/x/log.txt?x=1754783f800000
kernel config: https://syzkaller.appspot.com/x/.config?x=a601a80fec461d44
dashboard link: https://syzkaller.appspot.com/bug?extid=76de61614cb1abdd73fc
compiler: gcc (GCC) 8.0.1 20180413 (experimental)
syzkaller repro:https://syzkaller.appspot.com/x/repro.syz?x=12c1e1bf800000
IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+76de61614cb1abdd73fc@syzkaller.appspotmail.com
IPv6: ADDRCONF(NETDEV_CHANGE): veth1: link becomes ready
IPv6: ADDRCONF(NETDEV_CHANGE): veth0: link becomes ready
8021q: adding VLAN 0 to HW filter on device team0
8021q: adding VLAN 0 to HW filter on device team0
==================================================================
BUG: KASAN: slab-out-of-bounds in skb_at_tc_ingress
include/net/sch_generic.h:535 [inline]
BUG: KASAN: slab-out-of-bounds in bpf_push_mac_rcsum net/core/filter.c:1625
[inline]
BUG: KASAN: slab-out-of-bounds in ____bpf_skb_vlan_push
net/core/filter.c:2446 [inline]
BUG: KASAN: slab-out-of-bounds in bpf_skb_vlan_push+0x6b7/0x720
net/core/filter.c:2437
Read of size 5 at addr ffff8801b77347d0 by task syz-executor6/6529
CPU: 1 PID: 6529 Comm: syz-executor6 Not tainted 4.17.0-rc7+ #38
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1b9/0x294 lib/dump_stack.c:113
print_address_description+0x6c/0x20b mm/kasan/report.c:256
kasan_report_error mm/kasan/report.c:354 [inline]
kasan_report.cold.7+0x242/0x2fe mm/kasan/report.c:412
__asan_report_load_n_noabort+0xf/0x20 mm/kasan/report.c:443
skb_at_tc_ingress include/net/sch_generic.h:535 [inline]
bpf_push_mac_rcsum net/core/filter.c:1625 [inline]
____bpf_skb_vlan_push net/core/filter.c:2446 [inline]
bpf_skb_vlan_push+0x6b7/0x720 net/core/filter.c:2437
Allocated by task 6523:
save_stack+0x43/0xd0 mm/kasan/kasan.c:448
set_track mm/kasan/kasan.c:460 [inline]
kasan_kmalloc+0xc4/0xe0 mm/kasan/kasan.c:553
kasan_slab_alloc+0x12/0x20 mm/kasan/kasan.c:490
kmem_cache_alloc_node+0x144/0x780 mm/slab.c:3644
__alloc_skb+0x111/0x780 net/core/skbuff.c:193
alloc_skb include/linux/skbuff.h:987 [inline]
netlink_alloc_large_skb net/netlink/af_netlink.c:1182 [inline]
netlink_sendmsg+0xb01/0xfa0 net/netlink/af_netlink.c:1876
sock_sendmsg_nosec net/socket.c:629 [inline]
sock_sendmsg+0xd5/0x120 net/socket.c:639
___sys_sendmsg+0x805/0x940 net/socket.c:2117
__sys_sendmsg+0x115/0x270 net/socket.c:2155
__do_sys_sendmsg net/socket.c:2164 [inline]
__se_sys_sendmsg net/socket.c:2162 [inline]
__x64_sys_sendmsg+0x78/0xb0 net/socket.c:2162
do_syscall_64+0x1b1/0x800 arch/x86/entry/common.c:287
entry_SYSCALL_64_after_hwframe+0x49/0xbe
Freed by task 5303:
save_stack+0x43/0xd0 mm/kasan/kasan.c:448
set_track mm/kasan/kasan.c:460 [inline]
__kasan_slab_free+0x11a/0x170 mm/kasan/kasan.c:521
kasan_slab_free+0xe/0x10 mm/kasan/kasan.c:528
__cache_free mm/slab.c:3498 [inline]
kmem_cache_free+0x86/0x2d0 mm/slab.c:3756
kfree_skbmem+0x13c/0x210 net/core/skbuff.c:582
__kfree_skb net/core/skbuff.c:642 [inline]
consume_skb+0x193/0x550 net/core/skbuff.c:701
netlink_dump+0xafa/0xd20 net/netlink/af_netlink.c:2262
netlink_recvmsg+0xe97/0x1450 net/netlink/af_netlink.c:1984
sock_recvmsg_nosec net/socket.c:802 [inline]
sock_recvmsg+0xd0/0x110 net/socket.c:809
___sys_recvmsg+0x2b6/0x680 net/socket.c:2279
__sys_recvmsg+0x112/0x260 net/socket.c:2328
__do_sys_recvmsg net/socket.c:2338 [inline]
__se_sys_recvmsg net/socket.c:2335 [inline]
__x64_sys_recvmsg+0x78/0xb0 net/socket.c:2335
do_syscall_64+0x1b1/0x800 arch/x86/entry/common.c:287
entry_SYSCALL_64_after_hwframe+0x49/0xbe
The buggy address belongs to the object at ffff8801b77346c0
which belongs to the cache skbuff_head_cache of size 232
The buggy address is located 40 bytes to the right of
232-byte region [ffff8801b77346c0, ffff8801b77347a8)
The buggy address belongs to the page:
page:ffffea0006ddcd00 count:1 mapcount:0 mapping:ffff8801b7734080 index:0x0
flags: 0x2fffc0000000100(slab)
raw: 02fffc0000000100 ffff8801b7734080 0000000000000000 000000010000000c
raw: ffffea00073a2da0 ffffea0006ddc060 ffff8801d942b840 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8801b7734680: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00
ffff8801b7734700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> ffff8801b7734780: 00 00 00 00 00 fc fc fc fc fc fc fc fc fc fc fc
^
ffff8801b7734800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8801b7734880: fb fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc
==================================================================
---
This bug is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this bug report. See:
https://goo.gl/tpsmEJ#bug-status-tracking for how to communicate with
syzbot.
syzbot can test patches for this bug, for details see:
https://goo.gl/tpsmEJ#testing-patches
^ permalink raw reply
* Re: [PATCH net/jkirsher] bpf, xdp, i40e: fix i40e_build_skb skb reserve and truesize
From: John Fastabend @ 2018-06-13 16:26 UTC (permalink / raw)
To: Daniel Borkmann, jeffrey.t.kirsher
Cc: intel-wired-lan, keith.busch, makita.toshiaki, bjorn.topel,
netdev
In-Reply-To: <20180613090436.4266-1-daniel@iogearbox.net>
On 06/13/2018 02:04 AM, Daniel Borkmann wrote:
> Using skb_reserve(skb, I40E_SKB_PAD + (xdp->data - xdp->data_hard_start))
> is clearly wrong since I40E_SKB_PAD already points to the offset where
> the original xdp->data was sitting since xdp->data_hard_start is defined
> as xdp->data - i40e_rx_offset(rx_ring) where latter offsets to I40E_SKB_PAD
> when build skb is used.
>
> However, also before cc5b114dcf98 ("bpf, i40e: add meta data support")
> this seems broken since bpf_xdp_adjust_head() helper could have been used
> to alter headroom and enlarge / shrink the frame and with that the assumption
> that the xdp->data remains unchanged does not hold and would push a bogus
> packet to upper stack.
>
> ixgbe got this right in 924708081629 ("ixgbe: add XDP support for pass and
> drop actions"). In any case, fix it by removing the I40E_SKB_PAD from both
> skb_reserve() and truesize calculation.
>
> Fixes: cc5b114dcf98 ("bpf, i40e: add meta data support")
> Fixes: 0c8493d90b6b ("i40e: add XDP support for pass and drop actions")
> Reported-by: Keith Busch <keith.busch@linux.intel.com>
> Reported-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> Cc: Björn Töpel <bjorn.topel@intel.com>
> Cc: John Fastabend <john.fastabend@gmail.com>
> ---
> drivers/net/ethernet/intel/i40e/i40e_txrx.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
Thanks! I missed this during review.
Acked-by: John Fastabend <john.fastabend@gmail.com>
^ permalink raw reply
* Re: [RFC PATCH 01/12] xen/manage: keep track of the on-going suspend mode
From: Balbir Singh @ 2018-06-13 16:42 UTC (permalink / raw)
To: Anchal Agarwal
Cc: Thomas Gleixner, Ingo Molnar, H. Peter Anvin,
maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), boris.ostrovsky,
konrad.wilk, roger.pau, netdev, jgross, xen-devel,
linux-kernel@vger.kernel.org, kamatam, Frank van der Linden,
vallish, guruanb, eduval, Rafael J. Wysocki, Pavel Machek,
Len Brown, linux-pm, cyberax
In-Reply-To: <20180612205619.28156-2-anchalag@amazon.com>
On Wed, Jun 13, 2018 at 6:56 AM, Anchal Agarwal <anchalag@amazon.com> wrote:
> From: Munehisa Kamata <kamatam@amazon.com>
>
> To differentiate between Xen suspend, PM suspend and PM hibernation,
> keep track of the on-going suspend mode by mainly using a new PM
> notifier. Since Xen suspend doesn't have corresponding PM event, its
> main logic is modfied to acquire pm_mutex and set the current mode.
>
Why do we need to differentiate between them? The changelog does not
explain how Xen Suspend is different from PM suspend. The difference
could be what is injected into the guest vs what the guest decides to
do. How do we use the new suspend_mode?
> Note that we may see deadlock if PM suspend/hibernation is interrupted
> by Xen suspend. PM suspend/hibernation depends on xenwatch thread to
> process xenbus state transactions, but the thread will sleep to wait
> pm_mutex which is already held by PM suspend/hibernation context in the
> scenario. Though, acquirng pm_mutex is still right thing to do, and we
> would need to modify Xen shutdown code to avoid the issue. This will be
> fixed by a separate patch.
>
> Signed-off-by: Munehisa Kamata <kamatam@amazon.com>
> Signed-off-by: Anchal Agarwal <anchalag@amazon.com>
> Reviewed-by: Sebastian Biemueller <sbiemue@amazon.com>
> Reviewed-by: Munehisa Kamata <kamatam@amazon.com>
> Reviewed-by: Eduardo Valentin <eduval@amazon.com>
> ---
> drivers/xen/manage.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
>
> diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
> index 8835065..8f9ea87 100644
> --- a/drivers/xen/manage.c
> +++ b/drivers/xen/manage.c
> @@ -13,6 +13,7 @@
> #include <linux/freezer.h>
> #include <linux/syscore_ops.h>
> #include <linux/export.h>
> +#include <linux/suspend.h>
>
> #include <xen/xen.h>
> #include <xen/xenbus.h>
> @@ -39,6 +40,16 @@ enum shutdown_state {
> /* Ignore multiple shutdown requests. */
> static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
>
> +enum suspend_modes {
> + NO_SUSPEND = 0,
> + XEN_SUSPEND,
> + PM_SUSPEND,
> + PM_HIBERNATION,
> +};
Why do the enums range across namespaces -- between NO, XEN and PM?
Can we please be consistent XEN_WATCH_SUSPEND, XEN_PM_SUSPEND. etc?
Balbir Singh.
^ permalink raw reply
* Re: [PATCH 2/2] ktime: helpers to convert between ktime and jiffies
From: Florian Fainelli @ 2018-06-13 16:51 UTC (permalink / raw)
To: Tejaswi Tanikella, Andrew Lunn; +Cc: netdev, davem
In-Reply-To: <20180613133557.GA15265@tejaswit-linux.qualcomm.com>
On 06/13/2018 06:35 AM, Tejaswi Tanikella wrote:
> On Tue, Jun 12, 2018 at 06:30:32PM +0200, Andrew Lunn wrote:
>> On Mon, Jun 11, 2018 at 05:22:28PM +0530, Tejaswi Tanikella wrote:
>>> Signed-off-by: Tejaswi Tanikella <tejaswit@codeaurora.org>
>>> ---
>>> include/linux/ktime.h | 4 ++++
>>> 1 file changed, 4 insertions(+)
>>>
>>> diff --git a/include/linux/ktime.h b/include/linux/ktime.h
>>> index 5b9fddb..4881483 100644
>>> --- a/include/linux/ktime.h
>>> +++ b/include/linux/ktime.h
>>> @@ -96,6 +96,10 @@ static inline ktime_t timeval_to_ktime(struct timeval tv)
>>> /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */
>>> #define ktime_to_ns(kt) (kt)
>>>
>>> +/* ktime to jiffies and back */
>>> +#define ktime_to_jiffies(kt) nsecs_to_jiffies(kt)
>>> +#define jiffies_to_ktime(j) jiffies_to_nsecs(j)
>>
>> Hi Tejaswi
>>
>> You should also add some users of these new helpers.
>>
>> Andrew
>
> Hi Andrew,
>
> I used them in the first patch.
Therefore you must swap the order of the patches in order not to break
bisectability (that is, each commit must build on its own).
Thanks!--
Florian
^ permalink raw reply
* Re: [PATCH 1/1] selftest: check tunnel type more accurately
From: Y Song @ 2018-06-13 16:53 UTC (permalink / raw)
To: Wang Jian; +Cc: Alexei Starovoitov, Daniel Borkmann, Shuah Khan, netdev
In-Reply-To: <CAP4sYWUcLeuGv=N2Ww8KvvyvG=2un8OH=zkn5n+kwrhkK+RRfA@mail.gmail.com>
On Wed, Jun 13, 2018 at 5:03 AM, Wang Jian <jianjian.wang1@gmail.com> wrote:
> Grep tunnel type directly to make sure 'ip' command supports it.
>
> Signed-off-by: Jian Wang <jianjian.wang1@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
^ permalink raw reply
* [RFC PATCH RESEND] tcp: avoid F-RTO if SACK and timestamps are disabled
From: Michal Kubecek @ 2018-06-13 16:55 UTC (permalink / raw)
To: netdev; +Cc: Eric Dumazet, Yuchung Cheng, Ilpo Jarvinen, linux-kernel
In-Reply-To: <20180613164802.99B89A09E2@unicorn.suse.cz>
When F-RTO algorithm (RFC 5682) is used on connection without both SACK and
timestamps (either because of (mis)configuration or because the other
endpoint does not advertise them), specific pattern loss can make RTO grow
exponentially until the sender is only able to send one packet per two
minutes (TCP_RTO_MAX).
One way to reproduce is to
- make sure the connection uses neither SACK nor timestamps
- let tp->reorder grow enough so that lost packets are retransmitted
after RTO (rather than when high_seq - snd_una > reorder * MSS)
- let the data flow stabilize
- drop multiple sender packets in "every second" pattern
- either there is no new data to send or acks received in response to new
data are also window updates (i.e. not dupacks by definition)
In this scenario, the sender keeps cycling between retransmitting first
lost packet (step 1 of RFC 5682), sending new data by (2b) and timing out
again. In this loop, the sender only gets
(a) acks for retransmitted segments (possibly together with old ones)
(b) window updates
Without timestamps, neither can be used for RTT estimator and without SACK,
we have no newly sacked segments to estimate RTT either. Therefore each
timeout doubles RTO and without usable RTT samples so that there is nothing
to counter the exponential growth.
While disabling both SACK and timestamps doesn't make any sense, the
resulting behaviour is so pathological that it deserves an improvement.
(Also, both can be disabled on the other side.) Avoid F-RTO algorithm in
case both SACK and timestamps are disabled so that the sender falls back to
traditional slow start retransmission.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
net/ipv4/tcp_input.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 355d3dffd021..ed603f987b72 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2001,7 +2001,8 @@ void tcp_enter_loss(struct sock *sk)
*/
tp->frto = net->ipv4.sysctl_tcp_frto &&
(new_recovery || icsk->icsk_retransmits) &&
- !inet_csk(sk)->icsk_mtup.probe_size;
+ !inet_csk(sk)->icsk_mtup.probe_size &&
+ (tcp_is_sack(tp) || tp->rx_opt.tstamp_ok);
}
/* If ACK arrived pointing to a remembered SACK, it means that our
--
2.17.1
^ permalink raw reply related
* Re: [RFC PATCH RESEND] tcp: avoid F-RTO if SACK and timestamps are disabled
From: Michal Kubecek @ 2018-06-13 16:57 UTC (permalink / raw)
To: netdev; +Cc: Eric Dumazet, Yuchung Cheng, Ilpo Jarvinen, linux-kernel
In-Reply-To: <20180613165543.0F92DA09E2@unicorn.suse.cz>
On Wed, Jun 13, 2018 at 06:55:43PM +0200, Michal Kubecek wrote:
> When F-RTO algorithm (RFC 5682) is used on connection without both SACK and
> timestamps (either because of (mis)configuration or because the other
> endpoint does not advertise them), specific pattern loss can make RTO grow
> exponentially until the sender is only able to send one packet per two
> minutes (TCP_RTO_MAX).
>
> One way to reproduce is to
>
> - make sure the connection uses neither SACK nor timestamps
> - let tp->reorder grow enough so that lost packets are retransmitted
> after RTO (rather than when high_seq - snd_una > reorder * MSS)
> - let the data flow stabilize
> - drop multiple sender packets in "every second" pattern
> - either there is no new data to send or acks received in response to new
> data are also window updates (i.e. not dupacks by definition)
>
> In this scenario, the sender keeps cycling between retransmitting first
> lost packet (step 1 of RFC 5682), sending new data by (2b) and timing out
> again. In this loop, the sender only gets
>
> (a) acks for retransmitted segments (possibly together with old ones)
> (b) window updates
>
> Without timestamps, neither can be used for RTT estimator and without SACK,
> we have no newly sacked segments to estimate RTT either. Therefore each
> timeout doubles RTO and without usable RTT samples so that there is nothing
> to counter the exponential growth.
>
> While disabling both SACK and timestamps doesn't make any sense, the
> resulting behaviour is so pathological that it deserves an improvement.
> (Also, both can be disabled on the other side.) Avoid F-RTO algorithm in
> case both SACK and timestamps are disabled so that the sender falls back to
> traditional slow start retransmission.
>
> Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
I was able to illustrate the issue using a packetdrill script. It cheats
a bit by setting net.ipv4.tcp_reordering to 30 so that it we can get to
the issue more quickly. In this case, we don't have more data to send
but it's not essential; the issue can be reproduced even with sending of
new data in F-RTO, it would only make everything more complicated.
I was able to run the same script on kernels 4.17-rc6, 4.12 (SLE15) and
4.4 (SLE12-SP2). Kernel 3.12 required minor modifications but not in the
important part (the slow start is a bit slower there).
---------------------------------------------------------------------------
--tolerance_usecs=10000
// flush cached TCP metrics
0.000 `ip tcp_metrics flush all`
+0.000 `sysctl -q net.ipv4.tcp_reordering=20`
// establish a connection
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 setsockopt(3, SOL_SOCKET, SO_SNDBUF, [131072], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+0.100 < S 0:0(0) win 40000 <mss 1000>
+0.000 > S. 0:0(0) ack 1 <mss 1460>
+0.100 < . 1:1(0) ack 1 win 40000
+0.000 accept(3, ..., ...) = 4
// Send 10 data segments.
+0.100 write(4, ..., 30000) = 30000
// For some reason (unknown yet), GSO packets are only 2000 bytes long
+0.000 > . 1:2001(2000) ack 1
+0.000 > . 2001:4001(2000) ack 1
+0.000 > . 4001:6001(2000) ack 1
+0.000 > . 6001:8001(2000) ack 1
+0.000 > . 8001:10001(2000) ack 1
+0.100 < . 1:1(0) ack 2001 win 38000
+0.000 > . 10001:12001(2000) ack 1
+0.000 > . 12001:14001(2000) ack 1
+0.001 < . 1:1(0) ack 4001 win 36000
+0.000 > . 14001:16001(2000) ack 1
+0.000 > . 16001:18001(2000) ack 1
+0.001 < . 1:1(0) ack 6001 win 34000
+0.000 > . 18001:20001(2000) ack 1
+0.000 > . 20001:22001(2000) ack 1
+0.001 < . 1:1(0) ack 8001 win 32000
+0.000 > . 22001:24001(2000) ack 1
+0.000 > . 24001:26001(2000) ack 1
+0.001 < . 1:1(0) ack 10001 win 30000
+0.000 > . 26001:28001(2000) ack 1
+0.000 > P. 28001:30001(2000) ack 1
// loss of 12001:13001, 14001:15001, ..., 28001:29001
+0.100 < . 1:1(0) ack 12001 win 30000 // original ack
+0.000 < . 1:1(0) ack 12001 win 30000 // 13001:14001
+0.000 < . 1:1(0) ack 12001 win 30000 // 15001:16001
+0.000 < . 1:1(0) ack 12001 win 30000 // 17001:18001
+0.000 < . 1:1(0) ack 12001 win 30000 // 19001:20001
+0.000 < . 1:1(0) ack 12001 win 30000 // 21001:22001
+0.000 < . 1:1(0) ack 12001 win 30000 // 13001:24001
+0.000 < . 1:1(0) ack 12001 win 30000 // 25001:26001
+0.000 < . 1:1(0) ack 12001 win 30000 // 27001:28001
+0.000 < . 1:1(0) ack 12001 win 30000 // 29001:30001
// RTO 300ms
+0.270~+0.330 > . 12001:13001(1000) ack 1
+0.100 < . 1:1(0) ack 14001 win 38000
// RTO 600ms
+0.540~+0.660 > . 14001:15001(1000) ack 1
+0.100 < . 1:1(0) ack 16001 win 38000
// RTO 1200ms
+1.050~+1.350 > . 16001:17001(1000) ack 1
+0.100 < . 1:1(0) ack 18001 win 38000
// RTO 2400ms
+2.100~+2.700 > . 18001:19001(1000) ack 1
+0.100 < . 1:1(0) ack 20001 win 38000
// RTO 4800ms
+4.200~+5.400 > . 20001:21001(1000) ack 1
+0.100 < . 1:1(0) ack 22001 win 38000
// RTO 9600ms
+8.400~+10.800 > . 22001:23001(1000) ack 1
+0.100 < . 1:1(0) ack 24001 win 38000
// RTO 19200ms
+16.800~+21.600 > . 24001:25001(1000) ack 1
+1.000 `sysctl -q net.ipv4.tcp_reordering=3`
---------------------------------------------------------------------------
And this is what happens on current snapshot of master branch with
either net.ipv4.tcp_frto=0 or with the RFC patch:
---------------------------------------------------------------------------
--tolerance_usecs=10000
// flush cached TCP metrics
0.000 `ip tcp_metrics flush all`
+0.000 `sysctl -q net.ipv4.tcp_reordering=20`
// establish a connection
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0.000 setsockopt(3, SOL_SOCKET, SO_SNDBUF, [131072], 4) = 0
+0.000 bind(3, ..., ...) = 0
+0.000 listen(3, 1) = 0
+0.100 < S 0:0(0) win 40000 <mss 1000>
+0.000 > S. 0:0(0) ack 1 <mss 1460>
+0.100 < . 1:1(0) ack 1 win 40000
+0.000 accept(3, ..., ...) = 4
// Send 10 data segments.
+0.100 write(4, ..., 30000) = 30000
// For some reason (unknown yet), GSO packets are only 2000 bytes long
+0.000 > . 1:2001(2000) ack 1
+0.000 > . 2001:4001(2000) ack 1
+0.000 > . 4001:6001(2000) ack 1
+0.000 > . 6001:8001(2000) ack 1
+0.000 > . 8001:10001(2000) ack 1
+0.100 < . 1:1(0) ack 2001 win 38000
+0.000 > . 10001:12001(2000) ack 1
+0.000 > . 12001:14001(2000) ack 1
+0.001 < . 1:1(0) ack 4001 win 36000
+0.000 > . 14001:16001(2000) ack 1
+0.000 > . 16001:18001(2000) ack 1
+0.001 < . 1:1(0) ack 6001 win 34000
+0.000 > . 18001:20001(2000) ack 1
+0.000 > . 20001:22001(2000) ack 1
+0.001 < . 1:1(0) ack 8001 win 32000
+0.000 > . 22001:24001(2000) ack 1
+0.000 > . 24001:26001(2000) ack 1
+0.001 < . 1:1(0) ack 10001 win 30000
+0.000 > . 26001:28001(2000) ack 1
+0.000 > P. 28001:30001(2000) ack 1
// loss of 12001:13001, 14001:15001, ..., 28001:29001
+0.100 < . 1:1(0) ack 12001 win 30000 // original ack
+0.000 < . 1:1(0) ack 12001 win 30000 // 13001:14001
+0.000 < . 1:1(0) ack 12001 win 30000 // 15001:16001
+0.000 < . 1:1(0) ack 12001 win 30000 // 17001:18001
+0.000 < . 1:1(0) ack 12001 win 30000 // 19001:20001
+0.000 < . 1:1(0) ack 12001 win 30000 // 21001:22001
+0.000 < . 1:1(0) ack 12001 win 30000 // 13001:24001
+0.000 < . 1:1(0) ack 12001 win 30000 // 25001:26001
+0.000 < . 1:1(0) ack 12001 win 30000 // 27001:28001
+0.000 < . 1:1(0) ack 12001 win 30000 // 29001:30001
// RTO 300ms
+0.270~+0.330 > . 12001:13001(1000) ack 1
+0.100 < . 1:1(0) ack 14001 win 38000
+0.000 > . 14001:16001(2000) ack 1
+0.000 > . 16001:17001(1000) ack 1
+0.100 < . 1:1(0) ack 16001 win 38000
+0.000 > . 17001:18001(1000) ack 1
+0.000 > . 18001:20001(2000) ack 1
+0.000 > . 20001:21001(1000) ack 1
+0.100 < . 1:1(0) ack 18001 win 38000
+0.001 < . 1:1(0) ack 20001 win 36000
+0.001 < . 1:1(0) ack 21001 win 35000
+0.000 > . 21001:22001(1000) ack 1
+0.000 > . 22001:24001(2000) ack 1
+0.000 > . 24001:25001(1000) ack 1
+0.000 > . 25001:26001(1000) ack 1
+0.000 > . 26001:28001(2000) ack 1
+0.000 > . 28001:29001(1000) ack 1
+0.000 > P. 29001:30001(1000) ack 1
+0.100 < . 1:1(0) ack 22001 win 38000
+0.001 < . 1:1(0) ack 24001 win 36000
+0.001 < . 1:1(0) ack 26001 win 34000
+0.001 < . 1:1(0) ack 28001 win 32000
+0.001 < . 1:1(0) ack 30001 win 30000
+1.000 `sysctl -q net.ipv4.tcp_reordering=3`
---------------------------------------------------------------------------
Michal Kubecek
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH net/jkirsher] bpf, xdp, i40e: fix i40e_build_skb skb reserve and truesize
From: Alexander Duyck @ 2018-06-13 17:10 UTC (permalink / raw)
To: John Fastabend
Cc: Daniel Borkmann, Jeff Kirsher, Netdev, Björn Töpel,
intel-wired-lan, keith.busch, makita.toshiaki
In-Reply-To: <d56adbb7-12df-c197-1976-3eb161af8484@gmail.com>
On Wed, Jun 13, 2018 at 9:26 AM, John Fastabend
<john.fastabend@gmail.com> wrote:
> On 06/13/2018 02:04 AM, Daniel Borkmann wrote:
>> Using skb_reserve(skb, I40E_SKB_PAD + (xdp->data - xdp->data_hard_start))
>> is clearly wrong since I40E_SKB_PAD already points to the offset where
>> the original xdp->data was sitting since xdp->data_hard_start is defined
>> as xdp->data - i40e_rx_offset(rx_ring) where latter offsets to I40E_SKB_PAD
>> when build skb is used.
>>
>> However, also before cc5b114dcf98 ("bpf, i40e: add meta data support")
>> this seems broken since bpf_xdp_adjust_head() helper could have been used
>> to alter headroom and enlarge / shrink the frame and with that the assumption
>> that the xdp->data remains unchanged does not hold and would push a bogus
>> packet to upper stack.
>>
>> ixgbe got this right in 924708081629 ("ixgbe: add XDP support for pass and
>> drop actions"). In any case, fix it by removing the I40E_SKB_PAD from both
>> skb_reserve() and truesize calculation.
>>
>> Fixes: cc5b114dcf98 ("bpf, i40e: add meta data support")
>> Fixes: 0c8493d90b6b ("i40e: add XDP support for pass and drop actions")
>> Reported-by: Keith Busch <keith.busch@linux.intel.com>
>> Reported-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
>> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: Björn Töpel <bjorn.topel@intel.com>
>> Cc: John Fastabend <john.fastabend@gmail.com>
>> ---
>> drivers/net/ethernet/intel/i40e/i40e_txrx.c | 7 +++----
>> 1 file changed, 3 insertions(+), 4 deletions(-)
>>
>
> Thanks! I missed this during review.
>
> Acked-by: John Fastabend <john.fastabend@gmail.com>
Looks good to me. Thanks for taking care of this.
Acked-by: Alexander Duyck <alexander.h.duyck@intel.com>
^ permalink raw reply
* [PATCH net] netfilter: ipv6: nf_defrag: reduce struct net memory waste
From: Eric Dumazet @ 2018-06-13 17:11 UTC (permalink / raw)
To: Pablo Neira Ayuso, Jozsef Kadlecsik, Florian Westphal
Cc: netfilter-devel, netdev, Eric Dumazet, Eric Dumazet
It is a waste of memory to use a full "struct netns_sysctl_ipv6"
while only one pointer is really used, considering netns_sysctl_ipv6
keeps growing.
Also, since "struct netns_frags" has cache line alignment,
it is better to move the frags_hdr pointer outside, otherwise
we spend a full cache line for this pointer.
This saves 192 bytes of memory per netns.
Fixes: c038a767cd69 ("ipv6: add a new namespace for nf_conntrack_reasm")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/net/net_namespace.h | 1 +
include/net/netns/ipv6.h | 1 -
net/ipv6/netfilter/nf_conntrack_reasm.c | 6 +++---
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 47e35cce3b648d696b127ed7bd643036128795f6..a71264d75d7f98d28f92dfd861ffe6e0d39c0198 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -128,6 +128,7 @@ struct net {
#endif
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
struct netns_nf_frag nf_frag;
+ struct ctl_table_header *nf_frag_frags_hdr;
#endif
struct sock *nfnl;
struct sock *nfnl_stash;
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index c978a31b0f846210b4c2a369af960d5349b5395a..762ac9931b6251152b6ee0e5780df0f7b073f3e6 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -109,7 +109,6 @@ struct netns_ipv6 {
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
struct netns_nf_frag {
- struct netns_sysctl_ipv6 sysctl;
struct netns_frags frags;
};
#endif
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 5e0332014c1738999e680c1853829f384e880284..a452d99c9f5281b5e5d7e6f0162611deeb82212d 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -107,7 +107,7 @@ static int nf_ct_frag6_sysctl_register(struct net *net)
if (hdr == NULL)
goto err_reg;
- net->nf_frag.sysctl.frags_hdr = hdr;
+ net->nf_frag_frags_hdr = hdr;
return 0;
err_reg:
@@ -121,8 +121,8 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
{
struct ctl_table *table;
- table = net->nf_frag.sysctl.frags_hdr->ctl_table_arg;
- unregister_net_sysctl_table(net->nf_frag.sysctl.frags_hdr);
+ table = net->nf_frag_frags_hdr->ctl_table_arg;
+ unregister_net_sysctl_table(net->nf_frag_frags_hdr);
if (!net_eq(net, &init_net))
kfree(table);
}
--
2.18.0.rc1.242.g61856ae69a-goog
^ 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