Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1420070 > unrolled thread
| Started by | Henrik Austad <henrik@austad.us> |
|---|---|
| First post | 2016-06-12 00:40 +0200 |
| Last post | 2016-06-12 10:40 +0200 |
| Articles | 20 on this page of 21 — 3 participants |
Back to article view | Back to linux.kernel
[very-RFC 0/8] TSN driver for the kernel Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
[very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
Re: [very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel David Miller <davem@davemloft.net> - 2016-06-12 01:00 +0200
Re: [very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
[very-RFC 7/8] AVB ALSA - Add ALSA shim for TSN Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
[very-RFC 1/8] TSN: add documentation Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
Re: [very-RFC 1/8] TSN: add documentation David Miller <davem@davemloft.net> - 2016-06-12 00:40 +0200
Re: [very-RFC 1/8] TSN: add documentation Henrik Austad <henrik@austad.us> - 2016-06-12 00:50 +0200
Re: [very-RFC 1/8] TSN: add documentation David Miller <davem@davemloft.net> - 2016-06-12 00:50 +0200
Re: [very-RFC 1/8] TSN: add documentation Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
Re: [very-RFC 1/8] TSN: add documentation Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
[very-RFC 8/8] MAINTAINERS: add TSN/AVB-entries Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
[very-RFC 6/8] Add TSN event-tracing Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
[very-RFC 4/8] Add TSN header for the driver Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
Re: [very-RFC 4/8] Add TSN header for the driver Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
Re: [very-RFC 4/8] Add TSN header for the driver Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
[very-RFC 3/8] Adding TSN-driver to Intel I210 controller Henrik Austad <henrik@austad.us> - 2016-06-12 00:40 +0200
Re: [very-RFC 3/8] Adding TSN-driver to Intel I210 controller Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
Re: [very-RFC 0/8] TSN driver for the kernel Henrik Austad <henrik@austad.us> - 2016-06-12 01:00 +0200
Re: [very-RFC 5/8] Add TSN machinery to drive the traffic from a shim over the network Joe Perches <joe@perches.com> - 2016-06-12 09:40 +0200
Re: [very-RFC 5/8] Add TSN machinery to drive the traffic from a shim over the network Henrik Austad <henrik@austad.us> - 2016-06-12 10:40 +0200
Page 1 of 2 [1] 2 Next page →
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 0/8] TSN driver for the kernel |
| Message-ID | <rIZJn-rN-5@gated-at.bofh.it> |
Hi all (series based on v4.7-rc2) This is a *very* early RFC for a TSN-driver in the kernel. It has been floating around in my repo for a while and I would appreciate some feedback on the overall design to avoid doing some major blunders. TSN: Time Sensitive Networking, formely known as AVB (Audio/Video Bridging). There are at least one AVB-driver (the AV-part of TSN) in the kernel already, however this driver aims to solve a wider scope as TSN can do much more than just audio. A very basic ALSA-driver is added to the end that allows you to play music between 2 machines using aplay in one end and arecord | aplay on the other (some fiddling required) We have plans for doing the same for v4l2 eventually (but there are other fishes to fry first). The same goes for a TSN_SOCK type approach as well. TSN is all about providing infrastructure. Allthough there are a few very interesting uses for TSN (reliable, deterministic network for audio and video), once you have that reliable link, you can do a lot more. Some notes on the design: The driver is directed via ConfigFS as we need userspace to handle stream-reservation (MSRP), discovery and enumeration (IEEE 1722.1) and whatever other management is needed. Once we have all the required attributes, we can create link using mkdir, and use write() to set the attributes. Once ready, specify the 'shim' (basically a thin wrapper between TSN and another subsystem) and we start pushing out frames. The network part: it ties directly into the rx-handler for receive and writes skb's using netdev_start_xmit(). This could probably be improved. 2 new fields in netdev_ops have been introduced, and the Intel igb-driver has been updated (as this is available as a PCI-e card). The igb-driver works-ish What remains - tie to (g)PTP properly, currently using ktime_get() for presentation time - get time from shim into TSN and vice versa - let shim create/manage buffer Henrik Austad (8): TSN: add documentation TSN: Add the standard formerly known as AVB to the kernel Adding TSN-driver to Intel I210 controller Add TSN header for the driver Add TSN machinery to drive the traffic from a shim over the network Add TSN event-tracing AVB ALSA - Add ALSA shim for TSN MAINTAINERS: add TSN/AVB-entries Documentation/TSN/tsn.txt | 147 +++++ MAINTAINERS | 14 + drivers/media/Kconfig | 15 + drivers/media/Makefile | 3 +- drivers/media/avb/Makefile | 5 + drivers/media/avb/avb_alsa.c | 742 +++++++++++++++++++++++ drivers/media/avb/tsn_iec61883.h | 124 ++++ drivers/net/ethernet/intel/Kconfig | 18 + drivers/net/ethernet/intel/igb/Makefile | 2 +- drivers/net/ethernet/intel/igb/igb.h | 19 + drivers/net/ethernet/intel/igb/igb_main.c | 10 +- drivers/net/ethernet/intel/igb/igb_tsn.c | 396 ++++++++++++ include/linux/netdevice.h | 32 + include/linux/tsn.h | 806 ++++++++++++++++++++++++ include/trace/events/tsn.h | 349 +++++++++++ net/Kconfig | 1 + net/Makefile | 1 + net/tsn/Kconfig | 32 + net/tsn/Makefile | 6 + net/tsn/tsn_configfs.c | 623 +++++++++++++++++++ net/tsn/tsn_core.c | 975 ++++++++++++++++++++++++++++++ net/tsn/tsn_header.c | 203 +++++++ net/tsn/tsn_internal.h | 383 ++++++++++++ net/tsn/tsn_net.c | 403 ++++++++++++ 24 files changed, 5306 insertions(+), 3 deletions(-) create mode 100644 Documentation/TSN/tsn.txt create mode 100644 drivers/media/avb/Makefile create mode 100644 drivers/media/avb/avb_alsa.c create mode 100644 drivers/media/avb/tsn_iec61883.h create mode 100644 drivers/net/ethernet/intel/igb/igb_tsn.c create mode 100644 include/linux/tsn.h create mode 100644 include/trace/events/tsn.h create mode 100644 net/tsn/Kconfig create mode 100644 net/tsn/Makefile create mode 100644 net/tsn/tsn_configfs.c create mode 100644 net/tsn/tsn_core.c create mode 100644 net/tsn/tsn_header.c create mode 100644 net/tsn/tsn_internal.h create mode 100644 net/tsn/tsn_net.c -- 2.7.4
[toc] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel |
| Message-ID | <rIZJn-rN-7@gated-at.bofh.it> |
| In reply to | #1420070 |
TSN provides a mechanism to create reliable, jitter-free, low latency
guaranteed bandwidth links over a local network. It does this by
reserving a path through the network. Support for TSN must be found in
both the NIC as well as in the network itself.
This adds required hooks into netdev_ops so that the core TSN driver can
use this when configuring a new NIC or setting up a new link.
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Henrik Austad <henrik@austad.us>
---
include/linux/netdevice.h | 32 ++++++++++++++++++++++++++++++++
net/Kconfig | 1 +
net/tsn/Kconfig | 32 ++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
create mode 100644 net/tsn/Kconfig
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f45929c..de025eb 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -109,6 +109,13 @@ enum netdev_tx {
};
typedef enum netdev_tx netdev_tx_t;
+#if IS_ENABLED(CONFIG_TSN)
+enum sr_class {
+ SR_CLASS_A = 1,
+ SR_CLASS_B = 2,
+};
+#endif
+
/*
* Current order: NETDEV_TX_MASK > NET_XMIT_MASK >= 0 is significant;
* hard_start_xmit() return < NET_XMIT_MASK means skb was consumed.
@@ -902,6 +909,22 @@ struct tc_to_netdev {
*
* void (*ndo_poll_controller)(struct net_device *dev);
*
+ * TSN functions (if CONFIG_TSN)
+ *
+ * int (*ndo_tsn_capable)(struct net_device *dev);
+ * If a particular device is capable of sustaining TSN traffic
+ * provided current configuration
+ * int (*ndo_tsn_link_configure)(struct net_device *dev,
+ * enum sr_class class,
+ * u16 framesize,
+ * u16 vid);
+ * - When a new TSN link is either added or removed, this is called to
+ * update the bandwidth for the particular stream-class
+ * - The framesize is the size of the _entire_ frame, not just the
+ * payload since the full size is required to allocate bandwidth through
+ * the credit based shaper in the NIC
+ * - the vlan_id is the configured vlan for TSN in this session.
+ *
* SR-IOV management functions.
* int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac);
* int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos);
@@ -1148,6 +1171,15 @@ struct net_device_ops {
#ifdef CONFIG_NET_RX_BUSY_POLL
int (*ndo_busy_poll)(struct napi_struct *dev);
#endif
+
+#if IS_ENABLED(CONFIG_TSN)
+ int (*ndo_tsn_capable)(struct net_device *dev);
+ int (*ndo_tsn_link_configure)(struct net_device *dev,
+ enum sr_class class,
+ u16 framesize,
+ u16 vid);
+#endif /* CONFIG_TSN */
+
int (*ndo_set_vf_mac)(struct net_device *dev,
int queue, u8 *mac);
int (*ndo_set_vf_vlan)(struct net_device *dev,
diff --git a/net/Kconfig b/net/Kconfig
index ff40562..fa9f691 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -215,6 +215,7 @@ source "net/802/Kconfig"
source "net/bridge/Kconfig"
source "net/dsa/Kconfig"
source "net/8021q/Kconfig"
+source "net/tsn/Kconfig"
source "net/decnet/Kconfig"
source "net/llc/Kconfig"
source "net/ipx/Kconfig"
diff --git a/net/tsn/Kconfig b/net/tsn/Kconfig
new file mode 100644
index 0000000..1fc3c1d
--- /dev/null
+++ b/net/tsn/Kconfig
@@ -0,0 +1,32 @@
+#
+# Configuration for 802.1 Time Sensitive Networking (TSN)
+#
+
+config TSN
+ tristate "802.1 TSN Support"
+ depends on VLAN_8021Q && PTP_1588_CLOCK && CONFIGFS_FS
+ ---help---
+ Select this if you want to enable TSN on capable interfaces.
+
+ TSN allows you to set up deterministic links on your LAN (only
+ L2 is currently supported). Once loaded, the driver will probe
+ all available interfaces if they are capable of supporting TSN
+ links.
+
+ Once loaded, a directory in configfs called tsn/ will expose
+ the capable NICs and allow userspace to create
+ links. Userspace must provide us with a StreamID as well as
+ reserving bandwidth through the network and once this is done,
+ a new link can be created by issuing a mkdir() in configfs and
+ updating the attributes for the new link.
+
+ TSN itself does not produce nor consume data, it is dependent
+ upon 'shims' doing this, which can be virtually anything. ALSA
+ is a good candidate.
+
+ For more information, refer to the TSN-documentation in the
+ kernel documentation repository.
+
+ The resulting module will be called 'tsn'
+
+ If unsure, say N.
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel |
| Message-ID | <rJ02K-yf-21@gated-at.bofh.it> |
| In reply to | #1420071 |
Not as a reply, fresh new patch postings. :-/
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 2/8] TSN: Add the standard formerly known as AVB to the kernel |
| Message-ID | <rJ02K-yf-23@gated-at.bofh.it> |
| In reply to | #1420071 |
clearing up netdev-typo
On Sun, Jun 12, 2016 at 12:22:15AM +0200, Henrik Austad wrote:
> TSN provides a mechanism to create reliable, jitter-free, low latency
> guaranteed bandwidth links over a local network. It does this by
> reserving a path through the network. Support for TSN must be found in
> both the NIC as well as in the network itself.
>
> This adds required hooks into netdev_ops so that the core TSN driver can
> use this when configuring a new NIC or setting up a new link.
>
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Henrik Austad <henrik@austad.us>
> ---
> include/linux/netdevice.h | 32 ++++++++++++++++++++++++++++++++
> net/Kconfig | 1 +
> net/tsn/Kconfig | 32 ++++++++++++++++++++++++++++++++
> 3 files changed, 65 insertions(+)
> create mode 100644 net/tsn/Kconfig
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index f45929c..de025eb 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -109,6 +109,13 @@ enum netdev_tx {
> };
> typedef enum netdev_tx netdev_tx_t;
>
> +#if IS_ENABLED(CONFIG_TSN)
> +enum sr_class {
> + SR_CLASS_A = 1,
> + SR_CLASS_B = 2,
> +};
> +#endif
> +
> /*
> * Current order: NETDEV_TX_MASK > NET_XMIT_MASK >= 0 is significant;
> * hard_start_xmit() return < NET_XMIT_MASK means skb was consumed.
> @@ -902,6 +909,22 @@ struct tc_to_netdev {
> *
> * void (*ndo_poll_controller)(struct net_device *dev);
> *
> + * TSN functions (if CONFIG_TSN)
> + *
> + * int (*ndo_tsn_capable)(struct net_device *dev);
> + * If a particular device is capable of sustaining TSN traffic
> + * provided current configuration
> + * int (*ndo_tsn_link_configure)(struct net_device *dev,
> + * enum sr_class class,
> + * u16 framesize,
> + * u16 vid);
> + * - When a new TSN link is either added or removed, this is called to
> + * update the bandwidth for the particular stream-class
> + * - The framesize is the size of the _entire_ frame, not just the
> + * payload since the full size is required to allocate bandwidth through
> + * the credit based shaper in the NIC
> + * - the vlan_id is the configured vlan for TSN in this session.
> + *
> * SR-IOV management functions.
> * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac);
> * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos);
> @@ -1148,6 +1171,15 @@ struct net_device_ops {
> #ifdef CONFIG_NET_RX_BUSY_POLL
> int (*ndo_busy_poll)(struct napi_struct *dev);
> #endif
> +
> +#if IS_ENABLED(CONFIG_TSN)
> + int (*ndo_tsn_capable)(struct net_device *dev);
> + int (*ndo_tsn_link_configure)(struct net_device *dev,
> + enum sr_class class,
> + u16 framesize,
> + u16 vid);
> +#endif /* CONFIG_TSN */
> +
> int (*ndo_set_vf_mac)(struct net_device *dev,
> int queue, u8 *mac);
> int (*ndo_set_vf_vlan)(struct net_device *dev,
> diff --git a/net/Kconfig b/net/Kconfig
> index ff40562..fa9f691 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -215,6 +215,7 @@ source "net/802/Kconfig"
> source "net/bridge/Kconfig"
> source "net/dsa/Kconfig"
> source "net/8021q/Kconfig"
> +source "net/tsn/Kconfig"
> source "net/decnet/Kconfig"
> source "net/llc/Kconfig"
> source "net/ipx/Kconfig"
> diff --git a/net/tsn/Kconfig b/net/tsn/Kconfig
> new file mode 100644
> index 0000000..1fc3c1d
> --- /dev/null
> +++ b/net/tsn/Kconfig
> @@ -0,0 +1,32 @@
> +#
> +# Configuration for 802.1 Time Sensitive Networking (TSN)
> +#
> +
> +config TSN
> + tristate "802.1 TSN Support"
> + depends on VLAN_8021Q && PTP_1588_CLOCK && CONFIGFS_FS
> + ---help---
> + Select this if you want to enable TSN on capable interfaces.
> +
> + TSN allows you to set up deterministic links on your LAN (only
> + L2 is currently supported). Once loaded, the driver will probe
> + all available interfaces if they are capable of supporting TSN
> + links.
> +
> + Once loaded, a directory in configfs called tsn/ will expose
> + the capable NICs and allow userspace to create
> + links. Userspace must provide us with a StreamID as well as
> + reserving bandwidth through the network and once this is done,
> + a new link can be created by issuing a mkdir() in configfs and
> + updating the attributes for the new link.
> +
> + TSN itself does not produce nor consume data, it is dependent
> + upon 'shims' doing this, which can be virtually anything. ALSA
> + is a good candidate.
> +
> + For more information, refer to the TSN-documentation in the
> + kernel documentation repository.
> +
> + The resulting module will be called 'tsn'
> +
> + If unsure, say N.
> --
> 2.7.4
>
--
Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 7/8] AVB ALSA - Add ALSA shim for TSN |
| Message-ID | <rIZJn-rN-9@gated-at.bofh.it> |
| In reply to | #1420070 |
From: Henrik Austad <haustad@cisco.com>
This exposes a *very* rudimentary and simplistic ALSA driver that hooks
into TSN to create a device for userspace.
It currently only supports 44.1/48kHz sampling, 2ch, S16_LE
Userspace is supposed to reserve bandwidth, find StreamID etc.
To use as a Talker:
mkdir /config/tsn/test/eth0/talker
cd /config/tsn/test/eth0/talker
echo 65535 > buffer_size
echo 08:00:27:08:9f:c3 > remote_mac
echo 42 > stream_id
echo alsa > enabled
aplay -Ddefault:CARD=avb -c2 -r48000 -fS16_LE /opt/rickroll.wav
The same applies to Listener and arecord.
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Mark Brown <broonie@kernel.org>
Signed-off-by: Henrik Austad <haustad@cisco.com>
---
drivers/media/Kconfig | 15 +
drivers/media/Makefile | 3 +-
drivers/media/avb/Makefile | 5 +
drivers/media/avb/avb_alsa.c | 742 +++++++++++++++++++++++++++++++++++++++
drivers/media/avb/tsn_iec61883.h | 124 +++++++
5 files changed, 888 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/avb/Makefile
create mode 100644 drivers/media/avb/avb_alsa.c
create mode 100644 drivers/media/avb/tsn_iec61883.h
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index a8518fb..14ad1d9 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -217,3 +217,18 @@ source "drivers/media/tuners/Kconfig"
source "drivers/media/dvb-frontends/Kconfig"
endif # MEDIA_SUPPORT
+
+config MEDIA_AVB_ALSA
+ tristate "ALSA part of AVB over TSN"
+ depends on TSN
+ help
+
+ Enable the ALSA device that hoooks into TSN and allows the
+ computer to send ethernet frames over the network carrying
+ audio-data to selected hosts.
+
+ This must be configured by userspace as MSRP and IEEE 1722.1
+ (discovery and enumeration) is not implemented within the
+ kernel.
+
+ If unsure, say N
\ No newline at end of file
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index e608bbc..a1ca09e 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -20,6 +20,7 @@ endif
obj-$(CONFIG_VIDEO_DEV) += v4l2-core/
obj-$(CONFIG_DVB_CORE) += dvb-core/
+obj-$(CONFIG_AVB) += avb/
# There are both core and drivers at RC subtree - merge before drivers
obj-y += rc/
@@ -30,4 +31,4 @@ obj-y += rc/
obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
obj-$(CONFIG_VIDEO_DEV) += radio/
-
+obj-$(CONFIG_MEDIA_AVB_ALSA) += avb/
diff --git a/drivers/media/avb/Makefile b/drivers/media/avb/Makefile
new file mode 100644
index 0000000..5d6302c
--- /dev/null
+++ b/drivers/media/avb/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the ALSA shim in AVB/TSN
+#
+
+obj-$(CONFIG_MEDIA_AVB_ALSA) += avb_alsa.o
diff --git a/drivers/media/avb/avb_alsa.c b/drivers/media/avb/avb_alsa.c
new file mode 100644
index 0000000..9aff7d3
--- /dev/null
+++ b/drivers/media/avb/avb_alsa.c
@@ -0,0 +1,742 @@
+/* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights
+ * reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <linux/tsn.h>
+#include "tsn_iec61883.h"
+
+struct avb_chip {
+ struct snd_card *card;
+ struct tsn_link *link;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+
+ /* Need a reference to this when we unregister the platform
+ * driver.
+ */
+ struct platform_device *device;
+
+ /* on first copy, we set a few values, use this to make sure we
+ * only do this once.
+ */
+ u8 first_copy;
+
+ u8 sample_size;
+ u8 channels;
+
+ /* current idx in 10ms set of frames
+ * class A: 80
+ * class B: 40
+ *
+ * This is mostly relevant for 44.1kHz samplefreq
+ */
+ u8 num_10ms_series;
+
+ u32 sample_freq;
+};
+
+/* currently, only playback is implemented in TSN layer
+ *
+
+ * FIXMEs: (should be set according to the active TSN link)
+ * - format
+ * - rates
+ * - channels
+ */
+static struct snd_pcm_hardware snd_avb_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .buffer_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static size_t snd_avb_copy_size(struct tsn_link *link);
+
+
+static int _set_chip_values(struct avb_chip *avb_chip,
+ struct snd_pcm_runtime *runtime)
+{
+ if (!avb_chip->first_copy)
+ return 0;
+
+
+ /*
+ * first copy, we now know that runtime has all the correct
+ * values set, we can grab channels and rate. Sample_size
+ * (runtime->format) is currently hard-coded to S16_LE.
+ */
+ avb_chip->channels = runtime->channels;
+ avb_chip->sample_freq = runtime->rate;
+ avb_chip->sample_size = 16;
+
+ if (snd_avb_copy_size(avb_chip->link) > avb_chip->link->max_payload_size) {
+ pr_err("%s: Resulting payload-size is larger (%zd) than available (%u)\n",
+ __func__, snd_avb_copy_size(avb_chip->link),
+ avb_chip->link->max_payload_size);
+ return -EINVAL;
+ }
+ avb_chip->first_copy = 0;
+ return 0;
+}
+
+static int _snd_avb_open(struct avb_chip *avb_chip,
+ struct snd_pcm_runtime *runtime)
+{
+ /*
+ * We do not know what some of these values are until we see the
+ * first copy. We set to sane defaults where we don't have exact
+ * content.
+ */
+ avb_chip->channels = 0;
+ avb_chip->sample_size = 0;
+ avb_chip->sample_freq = 0;
+ avb_chip->num_10ms_series = 0;
+ avb_chip->first_copy = 1;
+
+ runtime->hw = snd_avb_hw;
+ runtime->buffer_size = avb_chip->link->buffer_size;
+ return 0;
+}
+
+/*
+ * bytes_to_frames()
+ * frames_to_bytes()
+ *
+ * frames_to_bytes(runtime, runtrime->period_size);
+ *
+ * Interrupt callbacks:
+ * The field traonsfer_ack_begin and transfer_ack_end are called at the
+ * beginning and at the end of snd_pcm_period_elapsed(), respectively.
+ */
+static int snd_avb_playback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ int ret = 0;
+
+ /*
+ * we've opened the PCM before probe returned properly and
+ * stored link in the struct.
+ */
+ if (!avb_chip || !avb_chip->link) {
+ pr_err("%s: Chip-data or link not available, cannot continue\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (!avb_chip->link->estype_talker) {
+ pr_info("Link (%llu) not registered as Talker, cannot do playback\n",
+ avb_chip->link->stream_id);
+ return -EINVAL;
+ }
+
+ ret = _snd_avb_open(avb_chip, runtime);
+ if (ret < 0) {
+ pr_err("%s: Could not open playback-device (requested %d ch, %zd buffer)",
+ __func__, avb_chip->channels,
+ avb_chip->link->buffer_size);
+ return ret;
+ }
+ pr_info("%s: %d channel PCM stream opened successfully, buffersize: %zd\n",
+ __func__, avb_chip->channels, avb_chip->link->buffer_size);
+ return 0;
+}
+
+static int snd_avb_playback_close(struct snd_pcm_substream *substream)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+
+ tsn_lb_disable(avb_chip->link);
+
+ pr_info("%s: something happened\n", __func__);
+ return 0;
+}
+
+/*
+ * snd_avb snd_avb.0: BUG: ,
+ * pos = 12288,
+ * buffer size = 8192,
+ * period size = 2048
+ *
+ * Playback is when we *send* data to a remote speaker
+ */
+static int snd_avb_playback_copy(struct snd_pcm_substream *substream,
+ int channel,
+ snd_pcm_uframes_t pos,
+ void *src,
+ snd_pcm_uframes_t count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ size_t bytes;
+ int ret;
+
+ /*
+ * From alsadoc:
+ *
+ * You need to check the channel argument, and if it's -1, copy
+ * the whole channels. Otherwise, you have to copy only the
+ * specified channel. Please check isa/gus/gus_pcm.c as an
+ * example.
+ */
+ if (channel != -1) {
+ pr_err("%s: partial copy not supportet\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = _set_chip_values(avb_chip, runtime);
+ if (ret != 0)
+ return ret;
+
+ bytes = frames_to_bytes(runtime, count);
+ ret = tsn_buffer_write(avb_chip->link, src, bytes);
+ if (ret != bytes) {
+ pr_err("%s: Incorrect copy (%zd, %d) corruption possible\n",
+ __func__, bytes, ret);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int snd_avb_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ int ret = 0;
+
+ if (!avb_chip || !avb_chip->link) {
+ pr_err("%s: Chip-data or link not available, cannot continue\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (avb_chip->link->estype_talker) {
+ pr_info("Link (%llu) registered as Talker, cannot capture\n",
+ avb_chip->link->stream_id);
+ return -EINVAL;
+ }
+ ret = _snd_avb_open(avb_chip, runtime);
+ if (ret < 0) {
+ pr_err("%s: Could not open capture-device (requested %d ch, %zd buffer)",
+ __func__, avb_chip->channels,
+ avb_chip->link->buffer_size);
+ return ret;
+ }
+ tsn_lb_enable(avb_chip->link);
+ pr_info("%s: %d channel PCM stream opened successfully, buffersize: %zd\n",
+ __func__, avb_chip->channels, avb_chip->link->buffer_size);
+ return 0;
+}
+
+static int snd_avb_capture_close(struct snd_pcm_substream *substream)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+
+ if (!avb_chip || !avb_chip->link)
+ return -EINVAL;
+ pr_err("%s: closing stream\n", __func__);
+
+ tsn_lb_disable(avb_chip->link);
+
+ return 0;
+}
+
+static int snd_avb_capture_copy(struct snd_pcm_substream *substream,
+ int channel,
+ snd_pcm_uframes_t pos,
+ void *src,
+ snd_pcm_uframes_t count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ size_t bytes;
+ int ret;
+
+ bytes = frames_to_bytes(runtime, count);
+ ret = tsn_buffer_read(avb_chip->link, src, bytes);
+ if (ret != bytes) {
+ pr_err("%s: incorrect copy (%zd, %d), corrupt capture possible\n",
+ __func__, bytes, ret);
+ tsn_lb_disable(avb_chip->link);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int snd_avb_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ /* FIXME, should do more than nothing */
+ return 0;
+}
+
+/*
+ * Called when the client defines buffer_size, period_size, format etc
+ * for the pcm substream.
+ *
+ * This is where link->buffer is allocated and link->buffer_size is
+ * defined.
+ *
+ * We are called in the beginning of snd_pcm_hw_params in
+ * sound/core/pcm_native.c, we cannot override runtime-values as they
+ * are updated from hw_params.
+ */
+static int snd_avb_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ unsigned int bsize = params_buffer_bytes(hw_params);
+ int ret = 0;
+
+ /* We need this reference for the refill callback so that we can
+ * call snd_pcm_period_elapsed();
+ */
+ avb_chip->substream = substream;
+ ret = tsn_set_buffer_size(avb_chip->link, bsize);
+ if (ret < 0) {
+ pr_err("%s: could not set buffer_size (alsa requested too large? (%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ avb_chip->num_10ms_series = 0;
+ pr_info("%s: successfully set hw-params\n", __func__);
+out:
+ return ret;
+}
+
+static int snd_avb_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+
+ if (!avb_chip || !avb_chip->link)
+ return -EINVAL;
+ tsn_clear_buffer_size(avb_chip->link);
+ pr_info("%s: something happened\n", __func__);
+ avb_chip->substream = NULL;
+ return 0;
+}
+
+static int snd_avb_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ /* verify that samplerate, freq and size is what we have set in
+ * the link.
+ */
+
+ return 0;
+}
+
+/*
+ * When the PCM stream is started, stopped, paused etc.
+ *
+ * Atomic function (some lock is being held by PCM layer)
+ */
+static int snd_avb_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* pr_err("%s: starting for some reason\n", __func__); */
+ tsn_lb_enable(avb_chip->link);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /* memset buffer to 0 */
+ /* pr_err("%s: stopping for some reason\n", __func__); */
+ tsn_lb_disable(avb_chip->link);
+ break;
+ default:
+ pr_info("%s: cmd: %d (return -EINVAL)\n", __func__, cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * current hw-position in the buffer, in frames from 0 to buffer_size -1
+ *
+ * Need to know where the hw-pointer is and how this corresponds to the
+ * underlying TSN-buffer setup
+ *
+ * Atomic function (some lock is being held by PCM layer)
+ *
+ */
+static snd_pcm_uframes_t snd_avb_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+ struct tsn_link *link = avb_chip->link;
+ snd_pcm_uframes_t pointer;
+
+ if (link->estype_talker)
+ pointer = bytes_to_frames(substream->runtime,
+ link->tail - link->buffer);
+ else
+ pointer = bytes_to_frames(substream->runtime,
+ link->head - link->buffer);
+ return pointer;
+}
+
+static struct snd_pcm_ops snd_avb_playback_ops = {
+ .open = snd_avb_playback_open,
+ .close = snd_avb_playback_close,
+ .copy = snd_avb_playback_copy,
+ .silence = snd_avb_silence,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_avb_pcm_hw_params,
+ .hw_free = snd_avb_pcm_hw_free,
+ .prepare = snd_avb_pcm_prepare,
+ .trigger = snd_avb_pcm_trigger,
+ .pointer = snd_avb_pcm_pointer,
+};
+
+static struct snd_pcm_ops snd_avb_capture_ops = {
+ .open = snd_avb_capture_open,
+ .close = snd_avb_capture_close,
+ .copy = snd_avb_capture_copy,
+ .silence = snd_avb_silence,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_avb_pcm_hw_params,
+ .hw_free = snd_avb_pcm_hw_free,
+ .prepare = snd_avb_pcm_prepare,
+ .trigger = snd_avb_pcm_trigger,
+ .pointer = snd_avb_pcm_pointer,
+};
+
+/*
+ * Callback for tsn_core for moving data into the buffer.
+ *
+ * This should be a wrapper (replace it with) the refill-functionality ALSA use.
+ */
+static size_t snd_avb_refill(struct tsn_link *link)
+{
+ struct avb_chip *avb_chip = link->media_chip;
+
+ if (avb_chip && avb_chip->substream) {
+ snd_pcm_period_elapsed(avb_chip->substream);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static size_t snd_avb_drain(struct tsn_link *link)
+{
+ struct avb_chip *avb_chip = link->media_chip;
+
+ if (avb_chip && avb_chip->substream) {
+ snd_pcm_period_elapsed(avb_chip->substream);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static size_t snd_avb_hdr_size(struct tsn_link *link)
+{
+ /* return the size of the iec61883-6 audio header */
+ return _iec61883_hdr_len();
+}
+
+static size_t snd_avb_copy_size(struct tsn_link *link)
+{
+ struct avb_chip *chip = link->media_chip;
+ /* use values in avb_chip, not link */
+ size_t framesize = (chip->sample_size >> 3) * chip->channels;
+ size_t numframes = 0;
+
+ if (!chip->sample_freq)
+ return link->max_payload_size;
+
+ /* size of each frame (samples per frame, sample-size && class)
+ * sample_size: 16 -> 2
+ * spframe : 12 (class b)
+ * channels: 2
+ *
+ * framesize: 2*12*2 -> 48
+ */
+
+ switch (chip->sample_freq) {
+ case 44100:
+ /*
+ * Class B: 40 frames, first 12 bytes, next 39 should be 11
+ */
+ if (!link->class_a) {
+ numframes = (chip->num_10ms_series ? 11 : 12);
+ chip->num_10ms_series++;
+ if (chip->num_10ms_series > 39)
+ chip->num_10ms_series = 0;
+ } else {
+ /* Class A slightly more involved
+ * Need 41 6 bytes and 39 5 bytes
+ *
+ * If 0th is set to 6, remaining odd idx should
+ * be 6, even (except 0th) to be 6
+ */
+ numframes = 5;
+ if (!chip->num_10ms_series ||
+ (chip->num_10ms_series % 0x2))
+ numframes++;
+ chip->num_10ms_series++;
+ if (chip->num_10ms_series > 79)
+ chip->num_10ms_series = 0;
+ }
+ break;
+ case 48000:
+ numframes = (link->class_a ? 6 : 12);
+ break;
+ default:
+ pr_err("Unsupported sample_freq (%d), disabling link\n",
+ chip->sample_freq);
+ tsn_lb_disable(link);
+ return -EINVAL;
+ }
+ return numframes * framesize;
+}
+
+static void snd_avb_assemble_iidc(struct tsn_link *link,
+ struct avtpdu_header *header, size_t bytes)
+{
+ _iec61883_hdr_assemble(header, bytes);
+}
+
+static int snd_avb_validate_iidc(struct tsn_link *link,
+ struct avtpdu_header *header)
+{
+ return _iec61883_hdr_verify(header);
+}
+
+static void *snd_avb_get_payload_data(struct tsn_link *link,
+ struct avtpdu_header *header)
+{
+ return _iec61883_payload(header);
+}
+
+static int snd_avb_new_pcm(struct avb_chip *avb_chip, int device)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(avb_chip->card, "AVB PCM", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = avb_chip;
+ strcpy(pcm->name, "AVB PCM");
+ avb_chip->pcm = pcm;
+
+ /* only playback at the moment, once we implement capture, we
+ * need to grab the Talker/Listener from TSN link
+ */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_avb_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_avb_capture_ops);
+
+ return 0;
+
+}
+static int snd_avb_probe(struct platform_device *devptr)
+{
+ int err;
+ struct snd_card *card;
+ struct avb_chip *avb_chip;
+
+ pr_info("%s: starting\n", __func__);
+
+ /*
+ * older kernel use snd_card_create. This is handled by
+ * tsn_compat.h in an attempt to make it easier to backport to
+ * older kernels.
+ */
+ err = snd_card_new(&devptr->dev, 1, "avb", THIS_MODULE,
+ sizeof(struct avb_chip), &card);
+ if (err < 0) {
+ pr_err("%s: trouble creating new card -> %d\n",
+ __func__, err);
+ return err;
+ }
+ avb_chip = card->private_data;
+ avb_chip->card = card;
+
+
+ /* create PCM device*/
+ err = snd_avb_new_pcm(avb_chip, 0);
+ if (err < 0) {
+ pr_err("%s: could not create new PCM device\n", __func__);
+ goto err_out;
+ }
+
+ /* register card */
+ pr_info("%s: ready to register card\n", __func__);
+ strcpy(card->driver, "Avb");
+ strcpy(card->shortname, "Avb");
+ sprintf(card->longname, "Avb %i", devptr->id + 1);
+ err = snd_card_register(card);
+ if (err < 0) {
+ pr_err("%s: Could not register card -> %d\n",
+ __func__, err);
+ snd_card_free(card);
+ return err;
+ }
+
+ if (err == 0) {
+ platform_set_drvdata(devptr, card);
+ pr_info("%s: Successfully initialized %s\n",
+ __func__, card->shortname);
+ return 0;
+ }
+err_out:
+ snd_card_free(card);
+ return err;
+}
+
+/*
+ * We are here as a result from being removed via
+ * tsn_link->shim_ops->media_close, which is snd_avb_close()
+ */
+static int snd_avb_remove(struct platform_device *devptr)
+{
+ struct snd_card *card = platform_get_drvdata(devptr);
+ struct avb_chip *avb_chip = card->private_data;
+
+ /* Make sure link holds no ref to this now dead card */
+ if (avb_chip && avb_chip->link) {
+ avb_chip->link->media_chip = NULL;
+ avb_chip->link = NULL;
+ }
+
+ /* call into link->ops->media_close() ? */
+ snd_card_free(card);
+ return 0;
+}
+
+static struct platform_driver snd_avb_driver = {
+ .probe = snd_avb_probe,
+ .remove = snd_avb_remove,
+ .driver = {
+ .name = "snd_avb",
+ .pm = NULL, /* don't care about Power Management */
+ },
+};
+
+static int snd_avb_close(struct tsn_link *link)
+{
+ struct avb_chip *avb_chip = link->media_chip;
+
+ if (!link->media_chip)
+ return 0;
+
+ pr_info("%s: Removing device\n", __func__);
+
+ platform_device_unregister(avb_chip->device);
+ /* platform unregister will call into snd_avb_remove */
+ platform_driver_unregister(&snd_avb_driver);
+
+ /* update link to remove pointer to now invalid memory */
+ link->media_chip = NULL;
+ return 0;
+}
+
+static int snd_avb_new(struct tsn_link *link)
+{
+ struct avb_chip *avb_chip;
+ struct snd_card *card;
+ struct platform_device *device;
+ int err;
+
+ err = platform_driver_register(&snd_avb_driver);
+ if (err < 0) {
+ pr_info("%s: trouble registering driver %d, unreg. partial driver and abort.\n",
+ __func__, err);
+ return err;
+ }
+
+ /*
+ * We only register a single card for now, look to
+ * /sys/devices/platform/snd_avb.0 for content.
+ *
+ * Probe will be triggered if name is same as .name in platform_driver
+ */
+ device = platform_device_register_simple("snd_avb", 0, NULL, 0);
+ if (IS_ERR(device)) {
+ pr_info("%s: ERROR registering simple platform-device\n",
+ __func__);
+ platform_driver_unregister(&snd_avb_driver);
+ return -ENODEV;
+ }
+
+ /* store data in driver so we can access it in .probe */
+ card = platform_get_drvdata(device);
+ if (card == NULL) {
+ pr_info("%s: Did not get anything from platform_get_drvdata()\n",
+ __func__);
+ platform_device_unregister(device);
+ return -ENODEV;
+ }
+ avb_chip = card->private_data;
+ avb_chip->device = device;
+ avb_chip->link = link;
+
+ link->media_chip = avb_chip;
+
+ return 0;
+}
+
+static struct tsn_shim_ops shim_ops = {
+ .shim_name = "alsa",
+ .probe = snd_avb_new,
+ .buffer_refill = snd_avb_refill,
+ .buffer_drain = snd_avb_drain,
+ .media_close = snd_avb_close,
+ .hdr_size = snd_avb_hdr_size,
+ .copy_size = snd_avb_copy_size,
+ .assemble_header = snd_avb_assemble_iidc,
+ .validate_header = snd_avb_validate_iidc,
+ .get_payload_data = snd_avb_get_payload_data,
+};
+
+static int __init avb_alsa_init(void)
+{
+ if (tsn_shim_register_ops(&shim_ops)) {
+ pr_err("Could not register ALSA-shim with TSN\n");
+ return -EINVAL;
+ }
+ pr_info("AVB ALSA added OK\n");
+ return 0;
+}
+
+static void __exit avb_alsa_exit(void)
+{
+ tsn_shim_deregister_ops(&shim_ops);
+}
+
+module_init(avb_alsa_init);
+module_exit(avb_alsa_exit);
+MODULE_AUTHOR("Henrik Austad");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TSN ALSA shim driver");
diff --git a/drivers/media/avb/tsn_iec61883.h b/drivers/media/avb/tsn_iec61883.h
new file mode 100644
index 0000000..bf26138
--- /dev/null
+++ b/drivers/media/avb/tsn_iec61883.h
@@ -0,0 +1,124 @@
+#ifndef TSN_IEC61883_H
+#define TSN_IEC61883_H
+#include <linux/tsn.h>
+
+/*
+ * psh:
+ * tag:2
+ * channel:6
+ * tcode:4
+ * sy:4
+ * See IEEE 1722.1 :: 6.2 for details
+ */
+struct iec61883_tag {
+ u8 tag:2;
+ u8 channel:6;
+ u8 tcode:4;
+ u8 sy:4;
+} __packed;
+
+struct iec61883_audio_header {
+ u8 sid:6;
+ u8 cip_1:2;
+
+ u8 dbs:8;
+
+ u8 rsv:2; /* reserved */
+ u8 sph:1;
+ u8 qpc:3;
+ u8 fn:2;
+
+ u8 dbc;
+
+ u8 fmt:6;
+ u8 cip_2:2;
+ u8 fdf;
+ u16 syt;
+ u8 payload[0];
+} __packed;
+
+static inline size_t _iec61883_hdr_len(void)
+{
+ return sizeof(struct iec61883_audio_header);
+}
+
+static inline int _iec61883_hdr_verify(struct avtpdu_header *hdr)
+{
+ struct iec61883_audio_header *dh;
+ struct iec61883_tag *psh;
+
+ if (hdr->subtype != AVTP_61883_IIDC)
+ return -EINVAL;
+ dh = (struct iec61883_audio_header *)&hdr->data;
+ psh = (struct iec61883_tag *)&hdr->psh;
+
+ /* Verify 61883 header */
+ if (psh->tag != 1 || psh->channel != 31 ||
+ psh->tcode != 0xA || psh->sy != 0)
+ return -EINVAL;
+
+ /* check flags that should be static from frame to frame */
+ if (dh->cip_1 != 0 || dh->sid != 0x3f || dh->qpc != 0 || dh->fn != 0 ||
+ dh->sph != 0 || dh->cip_2 != 2)
+ return -EINVAL;
+
+ if (dh->dbs != ntohs(hdr->sd_len)*2 || dh->dbc != hdr->seqnr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void _iec61883_hdr_assemble(struct avtpdu_header *hdr,
+ size_t bytes)
+{
+ struct iec61883_tag *psh;
+ struct iec61883_audio_header *dh;
+
+ if (bytes > 0x7f)
+ pr_warn("%s: hdr->dbs will overflow, malformed frame will be the result\n",
+ __func__);
+
+
+ hdr->subtype = AVTP_61883_IIDC;
+
+ /* IIDC 61883 header */
+ psh = (struct iec61883_tag *)&hdr->psh;
+ psh->tag = 1;
+ psh->channel = 31; /* 0x1f */
+ psh->tcode = 0xA;
+ psh->sy = 0;
+
+ dh = (struct iec61883_audio_header *)&hdr->data;
+ dh->cip_1 = 0;
+ dh->sid = 63; /* 0x3f */
+ dh->dbs = (u8)(bytes*2); /* number of quadlets of data in AVTPDU */
+ dh->qpc = 0;
+ dh->fn = 0;
+ dh->sph = 0;
+ dh->dbc = hdr->seqnr;
+ dh->cip_2 = 2;
+
+ /*
+ * FMT (Format ID): same as specified in iec 61883-1:2003
+ *
+ * For IEC 61883-6, it shall be 0x10 (16) to define Audio and
+ * Music data
+ */
+ dh->fmt = 0x10;
+
+ /* FIXME: find value
+ * Could be sampling-freq, but 8 bits give 0 - 65kHz sampling.
+ */
+ dh->fdf = 0;
+
+ dh->syt = 0xFFFF;
+}
+
+static inline void *_iec61883_payload(struct avtpdu_header *hdr)
+{
+ struct iec61883_audio_header *dh = (struct iec61883_audio_header *)&hdr->data;
+ /* TODO: add some basic checks before returning payload ? */
+ return &dh->payload;
+}
+
+#endif /* TSN_IEC61883_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rIZJn-rN-11@gated-at.bofh.it> |
| In reply to | #1420070 |
From: Henrik Austad <haustad@cisco.com> Describe the overall design behind the TSN standard, the TSN-driver, requirements to userspace and new functionality introduced. Cc: "David S. Miller" <davem@davemloft.net> Signed-off-by: Henrik Austad <haustad@cisco.com> --- Documentation/TSN/tsn.txt | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 Documentation/TSN/tsn.txt Index: linux/Documentation/TSN/tsn.txt =================================================================== --- /dev/null +++ linux/Documentation/TSN/tsn.txt @@ -0,0 +1,188 @@ + Time Sensitive Networking (TSN) + ------------------------------- + +[work in progress] + +1. Motivation +============= + +TSN is a set of open standards, formerly known as 'AVB' (Audio/Video +Bridging). It was renamed to TSN to better reflect that it can do much +more than just media transport. + +TSN is a way to create reliable streams across a network without loss of +frames due to congestion in the network. By using gPTP (a specialized +IEEE-1588v2 PTP profile), the time can be synchronized with sub-us +granularity across all the connected devices in the AVB domain. + +2. Intro to AVB/TSN +=================== + +The original standards were written with Audio/Video in mind, so the +initial standards refer to this as 'AVB'. In later standards, this has +changed to TSN, and AVB now refers to a service you can add on top of +TSN. Hopefully it will not be too confusing. + +In this document, we refer to the infrastructure part as TSN and AVB to +the ALSA/V4L2 shim which can be added on top of TSN to provide a +media-service. + +TSN operates with 'streams', and one stream can contain pretty much +whatever you like. Currently, only media has been defined properly +though, which is why you only have media-subtypes for the +avtp_subtype-field. + +For a media-setup, one stream can contain multiple channels, all going +to the same destination. A destination can be a single Listener +(singlecast) or a group of Listeners (multicast). + +2.1 Endpoints + +A TSN 'endpoint' is where a stream either originates or ends -what +others would call sources (Talkers) and sinks (Listeners). Looking back +at pre-TSN when this was called AVB, these names make a bit more sense. + +Common for both types, they need to be PTPv2 capable, i.e. you need to +timestamp gPTP frames upon ingress/egress to improve the accuracy of +PTP. + +2.1.1 Talkers + +Hardware requirements: +- Multiple Tx-queues +- Credit based shaper on at least one of the queues for pacing the + frames onto the network +- VLAN capable + +2.1.2 Listener + +A Listener does not have the same requirements as a Talker as it cannot +control the pace of the incoming frames anyway. It is beneficial if the +NIC understands VLANs and has a few Rx-queues so that you can steer all +TSN-frames to a dedicated queue. + +2.2 Bridges + +What TSN calls switches that are TSN-capable. They must be able to +prioritize TSN-streams, have the credit-based shaper available for that +class, support SRP, support gPTP and so on. + +2.3 Relevant standards + +* IEEE 802.1BA-2011 Audio Video Bridging (AVB) Systems + +* IEEE 802.1Q-2011 sec 34 and 35 + + What is referred to as: + IEEE 802.1Qav (Forwarding and Queueing for Time-sensitive Streams) + IEEE 802.1Qat (Stream Registration protocol) + +* IEEE 802.1AS gPTP + + A PTPv2 profile (from IEEE 1588) tailored for this domain. Notable + changes include the requirement that all nodes in the network must be + gPTP capable (i.e. no traversing non-PTP entities), and it allows + traffic over a wider range of medium that what "pure" PTPv2 allows. + +* IEEE 1722 AVTP Layer 2 Transport Protocol for Time-Sensitive + Applications in Bridged Local Area Networks + +* IEEE 1722.1 Device Discovery, Connection Management and Control for 1722 + + What allows AVB (TSN) devices to handle discovery, enumeration and + control, basically let you connect 2 devices from a 3rd + + In this (in the scope of the Linux kernel TSN driver) must be done + purely from userspace as we do not want the kernel to suddenly attach + to a remote system without the user's knowledge. This is further + reflected in how the attributes for the link is managed via ConfigFS. + + +3. Overview and/or design of the TSN-driver +=========================================== + +The driver handles the shifting of data for TSN-streams. Anything else +is left for userspace to handle. This includes stream reservation (using +some sort of MSRP client), negotiating multicast addresses, finding the +value of the different attributes and connect application(s) to the +exposed devices (currently we only have an ALSA-device). + + /--------------------\ + | | + | Media application | + | | + \--------------------/ + | | + +----------+ +----+ + | | + | | + +------------+ | + | ALSA | | + +------------+ | + | | + | | + +------------+ +--------------+ + | avb_alsa | | tsn_configfs | + | (tsn-shim) | +--------------+ + +------------+ | + | | + | | + +------+ | + | | + | | + +------------+ | + | tsn_core |<--------+ + +------------+ + | + | + +------------+ + | tsn_net | + +------------+ + | + | + +------------+ + | network | + | subsystem | + +------------+ + | + | + ... + + +3.1 Terms and concepts + +TSN uses the concept of streams and shims. + +- A shim is a thin wrapper that binds TSN to another subsystem (or + directly to userspace). avb_alsa is an example of such a shim. + +- A stream is the only data TSN cares about. What the data inside the + stream represents, is left for the associated shim to handle. TSN will + verify the headers up to the protocol specific header and then pass it + along to the shim. + +Note: currently, only the data-unit part is implemented, the control +part, in which 1722.1 (discovery and enumeration) is part, is not +handled. + +3.2 Userspace requirements + +(msrp-client, "tsnctl"-tool + +4. Creating a new link from userspace +===================================== + +[coming] + + +5. Creating a new shim +====================== + +shim_ops +[coming] + + +6. Other resources: +=================== + +https://en.wikipedia.org/wiki/Audio_Video_Bridging
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | Re: [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rIZJo-rN-31@gated-at.bofh.it> |
| In reply to | #1420073 |
Networking patches not CC:'d to netdev@vger.kernel.org are unlikely to be reviewed by networking developers at all.
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:50 +0200 |
| Subject | Re: [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rIZT3-v2-7@gated-at.bofh.it> |
| In reply to | #1420078 |
[Multipart message — attachments visible in raw view] — view raw
On Sat, Jun 11, 2016 at 03:35:10PM -0700, David Miller wrote: > > Networking patches not CC:'d to netdev@vger.kernel.org are unlikely to > be reviewed by networking developers at all. Oh no! I messed up git send-email and wrote linux-netdev@vger instead of netdev@vger. What would be the best approach? Resend series to netdev@vger? I don't want to spam too many lists either. -- Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2016-06-12 00:50 +0200 |
| Subject | Re: [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rIZT3-v2-15@gated-at.bofh.it> |
| In reply to | #1420083 |
From: Henrik Austad <henrik@austad.us> Date: Sun, 12 Jun 2016 00:47:28 +0200 > What would be the best approach? Resend series to netdev@vger? I don't want > to spam too many lists either. Resend to all the lists.
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rJ02K-yf-15@gated-at.bofh.it> |
| In reply to | #1420084 |
[Multipart message — attachments visible in raw view] — view raw
On Sat, Jun 11, 2016 at 03:49:42PM -0700, David Miller wrote: > From: Henrik Austad <henrik@austad.us> > Date: Sun, 12 Jun 2016 00:47:28 +0200 > > > What would be the best approach? Resend series to netdev@vger? I don't want > > to spam too many lists either. > > Resend to all the lists. ok, I'll do that then. Thanks -- Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 1/8] TSN: add documentation |
| Message-ID | <rJ02J-yf-7@gated-at.bofh.it> |
| In reply to | #1420073 |
On Sun, Jun 12, 2016 at 12:22:14AM +0200, Henrik Austad wrote: > From: Henrik Austad <haustad@cisco.com> Clearing up the netdev-typo > > Describe the overall design behind the TSN standard, the TSN-driver, > requirements to userspace and new functionality introduced. > > Cc: "David S. Miller" <davem@davemloft.net> > Signed-off-by: Henrik Austad <haustad@cisco.com> > --- > Documentation/TSN/tsn.txt | 147 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 147 insertions(+) > create mode 100644 Documentation/TSN/tsn.txt > > Index: linux/Documentation/TSN/tsn.txt > =================================================================== > --- /dev/null > +++ linux/Documentation/TSN/tsn.txt > @@ -0,0 +1,188 @@ > + Time Sensitive Networking (TSN) > + ------------------------------- > + > +[work in progress] > + > +1. Motivation > +============= > + > +TSN is a set of open standards, formerly known as 'AVB' (Audio/Video > +Bridging). It was renamed to TSN to better reflect that it can do much > +more than just media transport. > + > +TSN is a way to create reliable streams across a network without loss of > +frames due to congestion in the network. By using gPTP (a specialized > +IEEE-1588v2 PTP profile), the time can be synchronized with sub-us > +granularity across all the connected devices in the AVB domain. > + > +2. Intro to AVB/TSN > +=================== > + > +The original standards were written with Audio/Video in mind, so the > +initial standards refer to this as 'AVB'. In later standards, this has > +changed to TSN, and AVB now refers to a service you can add on top of > +TSN. Hopefully it will not be too confusing. > + > +In this document, we refer to the infrastructure part as TSN and AVB to > +the ALSA/V4L2 shim which can be added on top of TSN to provide a > +media-service. > + > +TSN operates with 'streams', and one stream can contain pretty much > +whatever you like. Currently, only media has been defined properly > +though, which is why you only have media-subtypes for the > +avtp_subtype-field. > + > +For a media-setup, one stream can contain multiple channels, all going > +to the same destination. A destination can be a single Listener > +(singlecast) or a group of Listeners (multicast). > + > +2.1 Endpoints > + > +A TSN 'endpoint' is where a stream either originates or ends -what > +others would call sources (Talkers) and sinks (Listeners). Looking back > +at pre-TSN when this was called AVB, these names make a bit more sense. > + > +Common for both types, they need to be PTPv2 capable, i.e. you need to > +timestamp gPTP frames upon ingress/egress to improve the accuracy of > +PTP. > + > +2.1.1 Talkers > + > +Hardware requirements: > +- Multiple Tx-queues > +- Credit based shaper on at least one of the queues for pacing the > + frames onto the network > +- VLAN capable > + > +2.1.2 Listener > + > +A Listener does not have the same requirements as a Talker as it cannot > +control the pace of the incoming frames anyway. It is beneficial if the > +NIC understands VLANs and has a few Rx-queues so that you can steer all > +TSN-frames to a dedicated queue. > + > +2.2 Bridges > + > +What TSN calls switches that are TSN-capable. They must be able to > +prioritize TSN-streams, have the credit-based shaper available for that > +class, support SRP, support gPTP and so on. > + > +2.3 Relevant standards > + > +* IEEE 802.1BA-2011 Audio Video Bridging (AVB) Systems > + > +* IEEE 802.1Q-2011 sec 34 and 35 > + > + What is referred to as: > + IEEE 802.1Qav (Forwarding and Queueing for Time-sensitive Streams) > + IEEE 802.1Qat (Stream Registration protocol) > + > +* IEEE 802.1AS gPTP > + > + A PTPv2 profile (from IEEE 1588) tailored for this domain. Notable > + changes include the requirement that all nodes in the network must be > + gPTP capable (i.e. no traversing non-PTP entities), and it allows > + traffic over a wider range of medium that what "pure" PTPv2 allows. > + > +* IEEE 1722 AVTP Layer 2 Transport Protocol for Time-Sensitive > + Applications in Bridged Local Area Networks > + > +* IEEE 1722.1 Device Discovery, Connection Management and Control for 1722 > + > + What allows AVB (TSN) devices to handle discovery, enumeration and > + control, basically let you connect 2 devices from a 3rd > + > + In this (in the scope of the Linux kernel TSN driver) must be done > + purely from userspace as we do not want the kernel to suddenly attach > + to a remote system without the user's knowledge. This is further > + reflected in how the attributes for the link is managed via ConfigFS. > + > + > +3. Overview and/or design of the TSN-driver > +=========================================== > + > +The driver handles the shifting of data for TSN-streams. Anything else > +is left for userspace to handle. This includes stream reservation (using > +some sort of MSRP client), negotiating multicast addresses, finding the > +value of the different attributes and connect application(s) to the > +exposed devices (currently we only have an ALSA-device). > + > + /--------------------\ > + | | > + | Media application | > + | | > + \--------------------/ > + | | > + +----------+ +----+ > + | | > + | | > + +------------+ | > + | ALSA | | > + +------------+ | > + | | > + | | > + +------------+ +--------------+ > + | avb_alsa | | tsn_configfs | > + | (tsn-shim) | +--------------+ > + +------------+ | > + | | > + | | > + +------+ | > + | | > + | | > + +------------+ | > + | tsn_core |<--------+ > + +------------+ > + | > + | > + +------------+ > + | tsn_net | > + +------------+ > + | > + | > + +------------+ > + | network | > + | subsystem | > + +------------+ > + | > + | > + ... > + > + > +3.1 Terms and concepts > + > +TSN uses the concept of streams and shims. > + > +- A shim is a thin wrapper that binds TSN to another subsystem (or > + directly to userspace). avb_alsa is an example of such a shim. > + > +- A stream is the only data TSN cares about. What the data inside the > + stream represents, is left for the associated shim to handle. TSN will > + verify the headers up to the protocol specific header and then pass it > + along to the shim. > + > +Note: currently, only the data-unit part is implemented, the control > +part, in which 1722.1 (discovery and enumeration) is part, is not > +handled. > + > +3.2 Userspace requirements > + > +(msrp-client, "tsnctl"-tool > + > +4. Creating a new link from userspace > +===================================== > + > +[coming] > + > + > +5. Creating a new shim > +====================== > + > +shim_ops > +[coming] > + > + > +6. Other resources: > +=================== > + > +https://en.wikipedia.org/wiki/Audio_Video_Bridging -- Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 8/8] MAINTAINERS: add TSN/AVB-entries |
| Message-ID | <rIZJn-rN-19@gated-at.bofh.it> |
| In reply to | #1420070 |
From: Henrik Austad <haustad@cisco.com> Not sure how relevant this is other than making a point about maintaining it. Signed-off-by: Henrik Austad <haustad@cisco.com> --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ed42cb6..ef5d926 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11634,6 +11634,20 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/tuners/tua9001* +TSN CORE DRIVER +M: Henrik Austad <haustad@cisco.com> +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/net/tsn/ +F: include/linux/tsn.h +F: include/trace/events/tsn.h + +TSN_AVB_DRIVER +M: Henrik Austad <haustad@cisco.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Supported +F: drivers/media/avb/ + TULIP NETWORK DRIVERS L: netdev@vger.kernel.org L: linux-parisc@vger.kernel.org -- 2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 6/8] Add TSN event-tracing |
| Message-ID | <rIZJn-rN-21@gated-at.bofh.it> |
| In reply to | #1420070 |
From: Henrik Austad <haustad@cisco.com>
This needs refactoring and should be updated to use TRACE_CLASS, but for
now it provides a fair debug-window into TSN.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Steven Rostedt <rostedt@goodmis.org> (maintainer:TRACING)
Cc: Ingo Molnar <mingo@redhat.com> (maintainer:TRACING)
Signed-off-by: Henrik Austad <haustad@cisco.com>
---
include/trace/events/tsn.h | 349 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 349 insertions(+)
create mode 100644 include/trace/events/tsn.h
diff --git a/include/trace/events/tsn.h b/include/trace/events/tsn.h
new file mode 100644
index 0000000..ac1f31b
--- /dev/null
+++ b/include/trace/events/tsn.h
@@ -0,0 +1,349 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tsn
+
+#if !defined(_TRACE_TSN_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TSN_H
+
+#include <linux/tsn.h>
+#include <linux/tracepoint.h>
+
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+/* #include <linux/skbuff.h> */
+
+/* FIXME: update to TRACE_CLASS to reduce overhead */
+TRACE_EVENT(tsn_buffer_write,
+
+ TP_PROTO(struct tsn_link *link,
+ size_t bytes),
+
+ TP_ARGS(link, bytes),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, size)
+ __field(size_t, bsize)
+ __field(size_t, size_left)
+ __field(void *, buffer)
+ __field(void *, head)
+ __field(void *, tail)
+ __field(void *, end)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->size = bytes;
+ __entry->bsize = link->used_buffer_size;
+ __entry->size_left = (link->head - link->tail) % link->used_buffer_size;
+ __entry->buffer = link->buffer;
+ __entry->head = link->head;
+ __entry->tail = link->tail;
+ __entry->end = link->end;
+ ),
+
+ TP_printk("stream_id=%llu, copy=%zd, buffer: %zd, avail=%zd, [buffer=%p, head=%p, tail=%p, end=%p]",
+ __entry->stream_id, __entry->size, __entry->bsize, __entry->size_left,
+ __entry->buffer, __entry->head, __entry->tail, __entry->end)
+
+ );
+
+TRACE_EVENT(tsn_buffer_write_net,
+
+ TP_PROTO(struct tsn_link *link,
+ size_t bytes),
+
+ TP_ARGS(link, bytes),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, size)
+ __field(size_t, bsize)
+ __field(size_t, size_left)
+ __field(void *, buffer)
+ __field(void *, head)
+ __field(void *, tail)
+ __field(void *, end)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->size = bytes;
+ __entry->bsize = link->used_buffer_size;
+ __entry->size_left = (link->head - link->tail) % link->used_buffer_size;
+ __entry->buffer = link->buffer;
+ __entry->head = link->head;
+ __entry->tail = link->tail;
+ __entry->end = link->end;
+ ),
+
+ TP_printk("stream_id=%llu, copy=%zd, buffer: %zd, avail=%zd, [buffer=%p, head=%p, tail=%p, end=%p]",
+ __entry->stream_id, __entry->size, __entry->bsize, __entry->size_left,
+ __entry->buffer, __entry->head, __entry->tail, __entry->end)
+
+ );
+
+
+TRACE_EVENT(tsn_buffer_read,
+
+ TP_PROTO(struct tsn_link *link,
+ size_t bytes),
+
+ TP_ARGS(link, bytes),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, size)
+ __field(size_t, bsize)
+ __field(size_t, size_left)
+ __field(void *, buffer)
+ __field(void *, head)
+ __field(void *, tail)
+ __field(void *, end)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->size = bytes;
+ __entry->bsize = link->used_buffer_size;
+ __entry->size_left = (link->head - link->tail) % link->used_buffer_size;
+ __entry->buffer = link->buffer;
+ __entry->head = link->head;
+ __entry->tail = link->tail;
+ __entry->end = link->end;
+ ),
+
+ TP_printk("stream_id=%llu, copy=%zd, buffer: %zd, avail=%zd, [buffer=%p, head=%p, tail=%p, end=%p]",
+ __entry->stream_id, __entry->size, __entry->bsize, __entry->size_left,
+ __entry->buffer, __entry->head, __entry->tail, __entry->end)
+
+ );
+
+TRACE_EVENT(tsn_refill,
+
+ TP_PROTO(struct tsn_link *link,
+ size_t reported_avail),
+
+ TP_ARGS(link, reported_avail),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, bsize)
+ __field(size_t, size_left)
+ __field(size_t, reported_left)
+ __field(size_t, low_water)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->bsize = link->used_buffer_size;
+ __entry->size_left = (link->head - link->tail) % link->used_buffer_size;
+ __entry->reported_left = reported_avail;
+ __entry->low_water = link->low_water_mark;
+ ),
+
+ TP_printk("stream_id=%llu, buffer=%zd, avail=%zd, reported=%zd, low=%zd",
+ __entry->stream_id, __entry->bsize, __entry->size_left, __entry->reported_left, __entry->low_water)
+ );
+
+TRACE_EVENT(tsn_send_batch,
+
+ TP_PROTO(struct tsn_link *link,
+ int num_send,
+ u64 ts_base_ns,
+ u64 ts_delta_ns),
+
+ TP_ARGS(link, num_send, ts_base_ns, ts_delta_ns),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(int, seqnr)
+ __field(int, num_send)
+ __field(u64, ts_base_ns)
+ __field(u64, ts_delta_ns)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->seqnr = (int)link->last_seqnr;
+ __entry->ts_base_ns = ts_base_ns;
+ __entry->ts_delta_ns = ts_delta_ns;
+ __entry->num_send = num_send;
+ ),
+
+ TP_printk("stream_id=%llu, seqnr=%d, num_send=%d, ts_base_ns=%llu, ts_delta_ns=%llu",
+ __entry->stream_id, __entry->seqnr, __entry->num_send, __entry->ts_base_ns, __entry->ts_delta_ns)
+ );
+
+
+TRACE_EVENT(tsn_rx_handler,
+
+ TP_PROTO(struct tsn_link *link,
+ const struct ethhdr *ethhdr,
+ u64 sid),
+
+ TP_ARGS(link, ethhdr, sid),
+
+ TP_STRUCT__entry(
+ __field(char *, name)
+ __field(u16, proto)
+ __field(u64, sid)
+ __field(u64, link_sid)
+ ),
+ TP_fast_assign(
+ __entry->name = link->nic->name;
+ __entry->proto = ethhdr->h_proto;
+ __entry->sid = sid;
+ __entry->link_sid = link->stream_id;
+ ),
+
+ TP_printk("name=%s, proto: 0x%04x, stream_id=%llu, link->sid=%llu",
+ __entry->name, ntohs(__entry->proto), __entry->sid, __entry->link_sid)
+ );
+
+TRACE_EVENT(tsn_du,
+
+ TP_PROTO(struct tsn_link *link,
+ size_t bytes),
+
+ TP_ARGS(link, bytes),
+
+ TP_STRUCT__entry(
+ __field(u64, link_sid)
+ __field(size_t, bytes)
+ ),
+ TP_fast_assign(
+ __entry->link_sid = link->stream_id;
+ __entry->bytes = bytes;
+ ),
+
+ TP_printk("stream_id=%llu,bytes=%zu",
+ __entry->link_sid, __entry->bytes)
+);
+
+TRACE_EVENT(tsn_set_buffer,
+
+ TP_PROTO(struct tsn_link *link, size_t bufsize),
+
+ TP_ARGS(link, bufsize),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, size)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->size = bufsize;
+ ),
+
+ TP_printk("stream_id=%llu,buffer_size=%zu",
+ __entry->stream_id, __entry->size)
+
+ );
+
+TRACE_EVENT(tsn_free_buffer,
+
+ TP_PROTO(struct tsn_link *link),
+
+ TP_ARGS(link),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, bufsize)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->bufsize = link->buffer_size;
+ ),
+
+ TP_printk("stream_id=%llu,size:%zd",
+ __entry->stream_id, __entry->bufsize)
+
+ );
+
+TRACE_EVENT(tsn_buffer_drain,
+
+ TP_PROTO(struct tsn_link *link, size_t used),
+
+ TP_ARGS(link, used),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(size_t, used)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->used = used;
+ ),
+
+ TP_printk("stream_id=%llu,used=%zu",
+ __entry->stream_id, __entry->used)
+
+);
+/* TODO: too long, need cleanup.
+ */
+TRACE_EVENT(tsn_pre_tx,
+
+ TP_PROTO(struct tsn_link *link, struct sk_buff *skb, size_t bytes),
+
+ TP_ARGS(link, skb, bytes),
+
+ TP_STRUCT__entry(
+ __field(u64, stream_id)
+ __field(u32, vlan_tag)
+ __field(size_t, bytes)
+ __field(size_t, data_len)
+ __field(unsigned int, headlen)
+ __field(u16, protocol)
+ __field(u16, prot_native)
+ __field(int, tx_idx)
+ __field(u16, mac_len)
+ __field(u16, hdr_len)
+ __field(u16, vlan_tci)
+ __field(u16, mac_header)
+ __field(unsigned int, tail)
+ __field(unsigned int, end)
+ __field(unsigned int, truesize)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_id = link->stream_id;
+ __entry->vlan_tag = (skb_vlan_tag_present(skb) ? skb_vlan_tag_get(skb) : 0);
+ __entry->bytes = bytes;
+ __entry->data_len = skb->data_len;
+ __entry->headlen = skb_headlen(skb);
+ __entry->protocol = ntohs(vlan_get_protocol(skb));
+ __entry->prot_native = ntohs(skb->protocol);
+ __entry->tx_idx = skb_get_queue_mapping(skb);
+
+ __entry->mac_len = skb->mac_len;
+ __entry->hdr_len = skb->hdr_len;
+ __entry->vlan_tci = skb->vlan_tci;
+ __entry->mac_header = skb->mac_header;
+ __entry->tail = (unsigned int)skb->tail;
+ __entry->end = (unsigned int)skb->end;
+ __entry->truesize = skb->truesize;
+ ),
+
+ TP_printk("stream_id=%llu,vlan_tag=0x%04x,data_size=%zd,data_len=%zd,headlen=%u,proto=0x%04x (0x%04x),tx_idx=%d,mac_len=%u,hdr_len=%u,vlan_tci=0x%02x,mac_header=0x%02x,tail=%u,end=%u,truesize=%u",
+ __entry->stream_id,
+ __entry->vlan_tag,
+ __entry->bytes,
+ __entry->data_len,
+ __entry->headlen,
+ __entry->protocol,
+ __entry->prot_native, __entry->tx_idx,
+ __entry->mac_len,
+ __entry->hdr_len,
+ __entry->vlan_tci,
+ __entry->mac_header,
+ __entry->tail,
+ __entry->end,
+ __entry->truesize)
+ );
+
+#endif /* _TRACE_TSN_H || TRACE_HEADER_MULTI_READ */
+
+#include <trace/define_trace.h>
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 4/8] Add TSN header for the driver |
| Message-ID | <rIZJo-rN-27@gated-at.bofh.it> |
| In reply to | #1420070 |
From: Henrik Austad <haustad@cisco.com>
This defines the general TSN headers for network packets, the
shim-interface and the central 'tsn_list' structure.
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Henrik Austad <haustad@cisco.com>
---
include/linux/tsn.h | 806 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 806 insertions(+)
create mode 100644 include/linux/tsn.h
diff --git a/include/linux/tsn.h b/include/linux/tsn.h
new file mode 100644
index 0000000..0e1f732b
--- /dev/null
+++ b/include/linux/tsn.h
@@ -0,0 +1,806 @@
+/* TSN - Time Sensitive Networking
+ *
+ * Copyright (C) 2016- Henrik Austad <haustad@cisco.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _TSN_H
+#define _TSN_H
+#include <linux/list.h>
+#include <linux/configfs.h>
+#include <linux/hrtimer.h>
+
+/* The naming here can be a bit confusing as we call it TSN but naming
+ * suggests 'AVB'. Reason: IEE 1722 was written before the working group
+ * was renamed to Time Sensitive Networking.
+ *
+ * To be precise. TSN describes the protocol for shipping data, AVB is a
+ * medialayer which you can build on top of TSN.
+ *
+ * For this reason the frames are given avb-names whereas the functions
+ * use tsn_-naming.
+ */
+
+/* 7 bit value 0x00 - 0x7F */
+enum avtp_subtype {
+ AVTP_61883_IIDC = 0,
+ AVTP_MMA = 0x1,
+ AVTP_MAAP = 0x7e,
+ AVTP_EXPERIMENTAL = 0x7f,
+};
+
+/* NOTE NOTE NOTE !!
+ * The headers below use bitfields extensively and verifications
+ * are needed when using little-endian vs big-endian systems.
+ */
+
+/* Common part of avtph header
+ *
+ * AVB Transport Protocol Common Header
+ *
+ * Defined in 1722-2011 Sec. 5.2
+ */
+struct avtp_ch {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ /* use avtp_subtype enum.
+ */
+ u8 subtype:7;
+
+ /* Controlframe: 1
+ * Dataframe : 0
+ */
+ u8 cd:1;
+
+ /* Type specific data, part 1 */
+ u8 tsd_1:4;
+
+ /* In current version of AVB, only 0 is valid, all other values
+ * are reserved for future versions.
+ */
+ u8 version:3;
+
+ /* Valid StreamID in frame
+ *
+ * ControlData not related to a specific stream should clear
+ * this (and have stream_id = 0), _all_ other values should set
+ * this to 1.
+ */
+ u8 sv:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 cd:1;
+ u8 subtype:7;
+ u8 sv:1;
+ u8 version:3;
+ u8 tsd_1:4;
+#else
+#error "Unknown Endianness, cannot determine bitfield ordering"
+#endif
+ /* Type specific data (adjacent to tsd_1, but split due to bitfield) */
+ u16 tsd_2;
+ u64 stream_id;
+
+ /*
+ * payload by subtype
+ */
+ u8 pbs[0];
+} __packed;
+
+/* AVTPDU Common Control header format
+ * IEEE 1722#5.3
+ */
+struct avtpc_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 subtype:7;
+ u8 cd:1;
+ u8 control_data:4;
+ u8 version:3;
+ u8 sv:1;
+ u16 control_data_length:11;
+ u16 status:5;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 cd:1;
+ u8 subtype:7;
+ u8 sv:1;
+ u8 version:3;
+ u8 control_data:4;
+ u16 status:5;
+ u16 control_data_length:11;
+#else
+#error "Unknown Endianness, cannot determine bitfield ordering"
+#endif
+ u64 stream_id;
+} __packed;
+
+/* AVTP common stream data AVTPDU header format
+ * IEEE 1722#5.4
+ */
+struct avtpdu_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 subtype:7;
+ u8 cd:1;
+
+ /* avtp_timestamp valid */
+ u8 tv: 1;
+
+ /* gateway_info valid */
+ u8 gv:1;
+
+ /* reserved */
+ u8 r:1;
+
+ /*
+ * Media clock Restart toggle
+ */
+ u8 mr:1;
+
+ u8 version:3;
+
+ /* StreamID valid */
+ u8 sv:1;
+ u8 seqnr;
+
+ /* Timestamp uncertain */
+ u8 tu:1;
+ u8 r2:7;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 cd:1;
+ u8 subtype:7;
+
+ u8 sv:1;
+ u8 version:3;
+ u8 mr:1;
+ u8 r:1;
+ u8 gv:1;
+ u8 tv: 1;
+
+ u8 seqnr;
+ u8 r2:7;
+ u8 tu:1;
+#else
+#error "Unknown Endianness, cannot determine bitfield ordering"
+#endif
+
+ u64 stream_id;
+
+ u32 avtp_timestamp;
+ u32 gateway_info;
+
+ /* Stream Data Length */
+ u16 sd_len;
+
+ /* Protocol specific header, derived from avtp_subtype */
+ u16 psh;
+
+ /* Stream Payload Data 0 to n octets
+ * n so that total size < MTU
+ */
+ u8 data[0];
+} __packed;
+
+
+/**
+ * struct tsn_list - The top level container of TSN
+ *
+ * This is what tsn_configfs refers to as 'tier-0'
+ *
+ * @head List of TSN cards
+ * @lock lock protecting global entries
+ * @tsn_subsys Ref to ConfigFS subsystem
+ *
+ * @running: hrtimer is running driving data out
+ * @tsn_timer: hrtimer container
+ * @num_avail Number of available TSN NICs exposed through ConfigFS
+ */
+struct tsn_list {
+ struct list_head head;
+ struct mutex lock;
+ struct configfs_subsystem tsn_subsys;
+
+ /*
+ * TSN-timer is running. Not to be confused with the per-link
+ * disabled flag which indicates if a remote client, like aplay,
+ * is pushing data to it.
+ */
+ atomic_t running;
+ struct hrtimer tsn_timer;
+ unsigned int period_ns;
+
+
+ size_t num_avail;
+};
+
+/**
+ * struct tsn_nic
+ *
+ * Individual TSN-capable NICs, or 'tier-1' struct
+ *
+ * @list linked list of all TSN NICs
+ * @group configfs group
+ * @dev corresponding net_device
+ * @dma_size : size of the DMA buffer
+ * @dma_handle: housekeeping DMA-stuff
+ * @dma_mem : pointer to memory region we're using for DMAing to the NIC
+ * @name Name of NIC (same as name in dev), TO BE REMOVED
+ * @txq Size of Tx-queue. TO BE REMOVED
+ * @rx_registered flag indicating if a handler is registered for the nic
+ * @capable: if the NIC is capable for proper TSN traffic or if it must
+ * be emulated in software.
+ *
+ */
+struct tsn_nic {
+ struct list_head list;
+ struct config_group group;
+ struct net_device *dev;
+ struct tsn_list *tsn_list;
+
+ size_t dma_size;
+ dma_addr_t dma_handle;
+ void *dma_mem;
+
+ char *name;
+ int txq;
+ u8 rx_registered:1;
+ u8 capable:1;
+ u8 reserved:6;
+};
+
+struct tsn_shim_ops;
+/**
+ * tsn_link - Structure describing a single TSN link
+ *
+ */
+struct tsn_link {
+ /*
+ * Lock for protecting the buffer
+ */
+ spinlock_t lock;
+
+ struct config_group group;
+ struct tsn_nic *nic;
+ struct hlist_node node;
+
+ /* The link itself is active, and the tsn_core will treat it as
+ * an active participant and feed data from it to the
+ * network. This places some restrictions on which attributes
+ * can be changed.
+ *
+ * 1: active
+ * 0: inactive
+ */
+ atomic_t active;
+
+ u64 timer_period_ns;
+
+ /* Pointer to media-specific data.
+ * e.g. struct avb_chip
+ */
+ void *media_chip;
+
+ u64 stream_id;
+
+ /*
+ * The max required size for a _single_ TSN frame.
+ *
+ * To be used instead of channels and sample_freq.
+ */
+ u16 max_payload_size;
+ u16 shim_header_size;
+
+ /*
+ * Size of buffer (in bytes) to use when handling data to/from
+ * NIC.
+ *
+ * Smaller size will result in client being called more often
+ * but also provides lower latencies.
+ */
+ size_t buffer_size;
+ size_t used_buffer_size;
+
+ /*
+ * Used when frames are constructed and shipped to the network
+ * layer. If this is true, 0-frames will be sent insted of data
+ * from the buffer.
+ */
+ atomic_t buffer_active;
+
+ /*
+ * ringbuffer for incoming or outging traffic
+ * +-----------------------------------+
+ * | ########## |
+ * +-----------------------------------+
+ * ^ ^ ^ ^
+ * buffer tail head end
+ *
+ * Buffer: start of memory area
+ * tail: first byte of data in buffer
+ * head: first unused slot in which to store new data
+ *
+ * head,tail is used to represent the position of 'live data' in
+ * the buffer.
+ */
+ void *buffer;
+ void *head;
+ void *tail;
+ void *end;
+
+ /* Number of bytes to run refill/drain callbacks */
+ size_t low_water_mark;
+ size_t high_water_mark;
+
+
+ /*
+ * callback ops.
+ */
+ struct tsn_shim_ops *ops;
+
+ /*
+ * EndStation Type
+ *
+ * Either Talker or Listener
+ *
+ * 1: We are *Talker*, i.e. producing data to send
+ * 0: We are *Listener*, i.e. we receive data from another ES.
+ *
+ * This is for a single link, so even though an end-station can
+ * be both Talker *and* Listener, a link can only be one.
+ */
+ u8 estype_talker;
+
+ /*
+ * Link will use buffer managed by the shim. For this to work,
+ * the shim must:
+ *
+ * - call tsn_use_external_buffer(link, size);
+ * - provide tsn_shim_buffer_swap(link) in tsn_shim_ops
+ */
+ u8 external_buffer;
+
+ u8 last_seqnr;
+
+ /*
+ * Class can be either A or B
+ *
+ * ClassA: every 125us
+ * ClassB: every 250us
+ *
+ * This will also affect how large each frame will be.
+ */
+ u8 class_a:1;
+
+ /*
+ * Any AVTP data stream must set the 802.1Q vlan id and priority
+ * Code point. This should be obtained from MSRP, default values
+ * are:
+ *
+ * pvid: SR_PVID 2
+ * pcp: Class A: 3
+ * Class B: 2
+ *
+ * See IEEE 802.1Q-2011, Sec 35.2.2.9.3 and table 6-6 in 6.6.2
+ * for details
+ */
+ u8 pcp_a:3;
+ u8 pcp_b:3;
+ u16 vlan_id:12;
+
+ u8 remote_mac[6];
+};
+
+/**
+ * tsn_link_on - make link active
+ *
+ * This cause most of the attributes to be treated read-only since we
+ * will have to re-negotiate with the network if most of these
+ * parameters change.
+ *
+ * Note: this means that the link will be handled by the rx-handler or
+ * the timer callback, but until the link_buffer is set active (via
+ * tsn_lb_on()), actual data is not moved.
+ *
+ * @link: link being set to active
+ */
+static inline void tsn_link_on(struct tsn_link *link)
+{
+ if (link)
+ atomic_set(&link->active, 1);
+}
+
+/**
+ * tsn_link_off - make link inactive
+ *
+ * The link will now be ignored by timer callback or the
+ * rx-handler. Attributes can be mostly freely changed (we assume that
+ * userspace sets values that are negotiated properly).
+ *
+ * @link: link to deactivate
+ */
+static inline void tsn_link_off(struct tsn_link *link)
+{
+ if (link)
+ atomic_set(&link->active, 0);
+}
+
+/**
+ * tsn_link_is_on - query link to see if it is active
+ *
+ * Mostly used by tsn_configfs to respect the "read-only" once link is
+ * configured and made active.
+ *
+ * @link active link
+ * @returns 1 if active/on, 0 otherwise
+ */
+static inline int tsn_link_is_on(struct tsn_link *link)
+{
+ if (link)
+ return atomic_read(&link->active);
+ return 0;
+}
+
+/**
+ * tsn_set_buffer_size - adjust buffersize to match a shim
+ *
+ * This will not allocate (or deallcoate) memory, just adjust how much
+ * of the buffer allocated in tsn_prepare_link is being used. tsn_
+ * expects tsn_clear_buffer_size() to be invoked when stream is closed.
+ */
+int tsn_set_buffer_size(struct tsn_link *link, size_t bsize);
+int tsn_clear_buffer_size(struct tsn_link *link);
+
+/**
+ * tsn_buffer_write write data into the buffer from shim
+ *
+ * This is called from the shim-driver when more data is available and
+ * data needs to be pushed out to the network.
+ *
+ * NOTE: This is used when TSN handles the databuffer. This will not be
+ * needed for "shim-hosted" buffers.
+ *
+ * _If_ this function is called when the link is inactive, it will
+ * _enable_ the link (i.e. link will mark the buffer as 'active'). Do
+ * not copy data into the buffer unless you are ready to start sending
+ * frames!
+ *
+ * @link active link
+ * @src the buffer to copy data from
+ * @bytes bytes to copy
+ * @return bytes copied from link->buffer or negative error
+ */
+int tsn_buffer_write(struct tsn_link *link, void *src, size_t bytes);
+
+
+/**
+ * tsn_buffer_read - read data from link->buffer and give to shim
+ *
+ * When we act as a listener, this is what the shim (should|will) call
+ * to grab data. It typically grabs much more data than the _net
+ * equivalent. It also do not trigger a refill-event the same way
+ * buffer_read_net does.
+ *
+ * @param link current link that holds the buffer
+ * @param buffer the buffer to copy into, must be at least of size bytes
+ * @param bytes number of bytes.
+ *
+ * Note that this routine does NOT CARE about channels, samplesize etc,
+ * it is a _pure_ copy that handles ringbuffer wraps etc.
+ *
+ * This function have side-effects as it will update internal tsn_link
+ * values.
+ *
+ * @return Bytes copied into link->buffer, negative value upon error.
+ */
+int tsn_buffer_read(struct tsn_link *link, void *buffer, size_t bytes);
+
+/**
+ * tsn_lb_enable - TSN Link Buffer Enable
+ *
+ * Mark the link as "buffer-enabled" which will let the core start
+ * shifting data in/out of the buffer instead of ignoring incoming
+ * frames or sending "nullframes".
+ *
+ * This is for the network-end of the tsn-buffer, i.e.
+ * - when enabled frames *from* the network will be inserted into the buffer,
+ * - or frames going *out* will include data from the buffer instead of sending
+ * null-frames.
+ *
+ * When disabled, data will be zero'd, e.g Tx will send NULL-frames and
+ * Rx will silently drop the frames.
+ *
+ * @link: active link
+ */
+static inline void tsn_lb_enable(struct tsn_link *link)
+{
+ if (link)
+ atomic_set(&link->buffer_active, 1);
+}
+
+/**
+ * tsn_lb_disable - stop using the buffer for the net-side of TSN
+ *
+ * When we close a stream, we do not necessarily tear down the link, and
+ * we need to handle the data in some way.
+ */
+static inline void tsn_lb_disable(struct tsn_link *link)
+{
+ if (link)
+ atomic_set(&link->buffer_active, 0);
+}
+
+/**
+ * tsn_lb() - query if we have disabled pushing of data to/from link-buffer
+ *
+ * @param struct tsn_link *link - active link
+ * @returns 1 if link is enabled
+ */
+static inline int tsn_lb(struct tsn_link *link)
+{
+ if (link)
+ return atomic_read(&link->buffer_active);
+
+ /* if link is NULL; buffer not active */
+ return 0;
+}
+
+
+/**
+ * Shim ops - what tsn_core use when calling back into the shim. All ops
+ * must be reentrant.
+ */
+#define SHIM_NAME_SIZE 32
+struct tsn_shim_ops {
+
+ /* internal linked list used by tsn_core to keep track of all
+ * shims.
+ */
+ struct list_head head;
+
+ /**
+ * name - a unique name identifying this shim
+ *
+ * This is what userspace use to indicate to core what SHIM a
+ * particular link will use. If the name is already present,
+ * core will reject this name.
+ */
+ char shim_name[SHIM_NAME_SIZE];
+
+ /**
+ * probe - callback when a new link of this type is instantiated.
+ *
+ * When a new link is brought online, this is called once the
+ * essential parts of tsn_core has finiesh. Once probe_cb has
+ * finisehd, the shim _must_ be ready to accept data to/from
+ * tsn_core. On the other hand, due to the final steps of setup,
+ * it cannot expect to be called into action immediately after
+ * probe has finished.
+ *
+ * In other words, shim must be ready, but core doesn't have to
+ *
+ * @param : a particular link to pass along to the probe-function.
+ */
+ int (*probe)(struct tsn_link *link);
+
+ /**
+ * buffer_swap - set a new buffer for the link. [OPTIONAL]
+ *
+ * Used when external buffering is enabled.
+ *
+ * When called, a new buffer must be returned WITHOUT blocking
+ * as this will be called from interrupt context.
+ *
+ * The buffer returned from the shim must be at least the size
+ * of used_buffer_size.
+ *
+ * @param current link
+ * @param old_buffer the buffer that are no longer needed
+ * @param used number of bytes in buffer that has been filled with data.
+ * @return new buffer to use
+ */
+ void * (*buffer_swap)(struct tsn_link *link, void *old_buffer,
+ size_t used);
+
+ /**
+ * buffer_refill - signal shim that more data is required
+ * @link Active link
+ *
+ * This function should not do anything that can preempt the
+ * task (kmalloc, sleeping lock) or invoke actions that can take
+ * a long time to complete.
+ *
+ * This will be called from tsn_buffer_read_net() when available
+ * data in the buffer drops below low_water_mark. It will be
+ * called with the link-lock *held*
+ */
+ size_t (*buffer_refill)(struct tsn_link *link);
+
+ /**
+ * buffer_drain - shim need to copy data from buffer
+ *
+ * This will be called from tsn_buffer_write_net() when data in
+ * the buffer exceeds high_water_mark.
+ *
+ * The expected behavior is for the shim to then fill data into
+ * the buffer via tsn_buffer_write()
+ */
+ size_t (*buffer_drain)(struct tsn_link *link);
+
+ /**
+ * media_close - shut down media controller properly
+ *
+ * when the link is closed/removed for some reason
+ * external to the media controller (ALSA soundcard, v4l2 driver
+ * etc), we call this to clean up.
+ *
+ * Normal operation is stopped before media_close is called, but
+ * all references should be valid. TSN core expects media_close
+ * to handle any local cleanup, once returned, any references in
+ * stale tsn_links cannot be trusted.
+ *
+ * @link: current link where data is stored
+ * @returns: 0 upon success, negative on error.
+ */
+ int (*media_close)(struct tsn_link *link);
+
+ /**
+ * hdr_size - ask shim how large the header is
+ *
+ * Needed when reserving space in skb for transmitting data.
+ *
+ * @link: current link where data is stored
+ * @return: size of header for this shim
+ */
+ size_t (*hdr_size)(struct tsn_link *link);
+
+ /**
+ * copy_size - ask client how much from the buffer to include in
+ * the next frame.
+ *
+ * This is for *outgoing* frames, incoming frames
+ * have 'sd_len' set in the header.
+ *
+ * Note: copy_size should not return a size larger
+ * than link->max_payload_size
+ */
+ size_t (*copy_size)(struct tsn_link *link);
+
+ /**
+ * validate_header - let the shim validate subtype-header
+ *
+ * Both psh and data may (or may not) contain headers that need
+ * validating. This is the responsibility of the shim to
+ * validate, and ops->valdiate_header() will be called before
+ * any data is copied from the incoming frame and into the
+ * buffer.
+ *
+ * Important: tsn_core expects validate_header to _not_ alter
+ * the contents of the frame, and ideally, validate_header could
+ * be called multiple times and give the same result.
+ *
+ * @param: active link owning the new data
+ * @param: start of data-unit header
+ *
+ * This function will be called from interrupt-context and MUST
+ * NOT take any locks.
+ */
+ int (*validate_header)(struct tsn_link *link,
+ struct avtpdu_header *header);
+
+ /**
+ * assemble_header - add shim-specific headers
+ *
+ * This adds the headers required by the current shim after the
+ * generic 1722-header.
+ *
+ * @param: active link
+ * @param: start of data-unit header
+ * @param: size of data to send in this frame
+ * @return void
+ */
+ void (*assemble_header)(struct tsn_link *link,
+ struct avtpdu_header *header, size_t bytes);
+
+ /**
+ * get_payload_data - get a pointer to where the data is stored
+ *
+ * core will use the pointer (or drop it if NULL is returned)
+ * and copy header->sd_len bytes of *consecutive* data from the
+ * target memory and into the buffer memory.
+ *
+ * This is called with relevant locks held, from interrupt context.
+ *
+ * @param link active link
+ * @param header header of frame, which contains data
+ * @returns pointer to memory to copy from
+ */
+ void * (*get_payload_data)(struct tsn_link *link,
+ struct avtpdu_header *header);
+};
+/**
+ * tsn_shim_register_ops - register shim-callbacks for a given shim
+ *
+ * @param shim_ops - callbacks. The ops-struct should be kept intact for
+ * as long as the driver is running.
+ *
+ *
+ */
+int tsn_shim_register_ops(struct tsn_shim_ops *shim_ops);
+
+/**
+ * tsn_shim_deregister_ops - remove callback for module
+ *
+ * Completely remove shim_ops. This will close any links currently using
+ * this shim. Note: the links will be closed, but _not_ removed.
+ *
+ * @param shim_ops ops associated with this shim
+ */
+void tsn_shim_deregister_ops(struct tsn_shim_ops *shim_ops);
+
+/**
+ * tsn_shim_get_active : return the name of the currently loaded shim
+ *
+ * @param current link
+ * @return name of shim (matches an entry from exported triggers)
+ */
+char *tsn_shim_get_active(struct tsn_link *link);
+
+/**
+ * tsn_shim_find_by_name find shim_ops by name
+ *
+ * @param name of shim
+ * @return shim or NULL if not found/error.
+ */
+struct tsn_shim_ops *tsn_shim_find_by_name(const char *name);
+
+/**
+ * tsn_shim_export_probe_triggers - export a list of registered shims
+ *
+ * @param page to write content into
+ * @returns length of data written to page
+ */
+ssize_t tsn_shim_export_probe_triggers(char *page);
+
+/**
+ * tsn_get_framesize - get the size of the next TSN frame to send
+ *
+ * This will call into the shim to get the next chunk of data to
+ * read. Some sanitychecking is performed, i.e.
+ *
+ * 0 <= size <= max_payload_size
+ *
+ * @param struct tsn_link *link active link
+ * @returns size of frame in bytes or negative on error.
+ */
+static inline size_t tsn_shim_get_framesize(struct tsn_link *link)
+{
+ size_t ret;
+
+ ret = link->ops->copy_size(link);
+ if (ret <= link->max_payload_size)
+ return ret;
+ return link->max_payload_size;
+}
+
+/**
+ * tsn_get_hdr_size - get the size of the shim-specific header size
+ *
+ * The shim will add it's own header to the frame.
+ */
+static inline size_t tsn_shim_get_hdr_size(struct tsn_link *link)
+{
+ size_t ret;
+
+ if (!link || !link->ops->hdr_size)
+ return -EINVAL;
+ ret = link->ops->hdr_size(link);
+ if (ret > link->max_payload_size)
+ return -EINVAL;
+ return ret;
+}
+
+#endif /* _TSN_H */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 4/8] Add TSN header for the driver |
| Message-ID | <rJ02J-yf-3@gated-at.bofh.it> |
| In reply to | #1420077 |
Clearing up netdev-typo
-H
On Sun, Jun 12, 2016 at 12:22:17AM +0200, Henrik Austad wrote:
> From: Henrik Austad <haustad@cisco.com>
>
> This defines the general TSN headers for network packets, the
> shim-interface and the central 'tsn_list' structure.
>
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Henrik Austad <haustad@cisco.com>
> ---
> include/linux/tsn.h | 806 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 806 insertions(+)
> create mode 100644 include/linux/tsn.h
>
> diff --git a/include/linux/tsn.h b/include/linux/tsn.h
> new file mode 100644
> index 0000000..0e1f732b
> --- /dev/null
> +++ b/include/linux/tsn.h
> @@ -0,0 +1,806 @@
> +/* TSN - Time Sensitive Networking
> + *
> + * Copyright (C) 2016- Henrik Austad <haustad@cisco.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _TSN_H
> +#define _TSN_H
> +#include <linux/list.h>
> +#include <linux/configfs.h>
> +#include <linux/hrtimer.h>
> +
> +/* The naming here can be a bit confusing as we call it TSN but naming
> + * suggests 'AVB'. Reason: IEE 1722 was written before the working group
> + * was renamed to Time Sensitive Networking.
> + *
> + * To be precise. TSN describes the protocol for shipping data, AVB is a
> + * medialayer which you can build on top of TSN.
> + *
> + * For this reason the frames are given avb-names whereas the functions
> + * use tsn_-naming.
> + */
> +
> +/* 7 bit value 0x00 - 0x7F */
> +enum avtp_subtype {
> + AVTP_61883_IIDC = 0,
> + AVTP_MMA = 0x1,
> + AVTP_MAAP = 0x7e,
> + AVTP_EXPERIMENTAL = 0x7f,
> +};
> +
> +/* NOTE NOTE NOTE !!
> + * The headers below use bitfields extensively and verifications
> + * are needed when using little-endian vs big-endian systems.
> + */
> +
> +/* Common part of avtph header
> + *
> + * AVB Transport Protocol Common Header
> + *
> + * Defined in 1722-2011 Sec. 5.2
> + */
> +struct avtp_ch {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + /* use avtp_subtype enum.
> + */
> + u8 subtype:7;
> +
> + /* Controlframe: 1
> + * Dataframe : 0
> + */
> + u8 cd:1;
> +
> + /* Type specific data, part 1 */
> + u8 tsd_1:4;
> +
> + /* In current version of AVB, only 0 is valid, all other values
> + * are reserved for future versions.
> + */
> + u8 version:3;
> +
> + /* Valid StreamID in frame
> + *
> + * ControlData not related to a specific stream should clear
> + * this (and have stream_id = 0), _all_ other values should set
> + * this to 1.
> + */
> + u8 sv:1;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> + u8 sv:1;
> + u8 version:3;
> + u8 tsd_1:4;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> + /* Type specific data (adjacent to tsd_1, but split due to bitfield) */
> + u16 tsd_2;
> + u64 stream_id;
> +
> + /*
> + * payload by subtype
> + */
> + u8 pbs[0];
> +} __packed;
> +
> +/* AVTPDU Common Control header format
> + * IEEE 1722#5.3
> + */
> +struct avtpc_header {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + u8 subtype:7;
> + u8 cd:1;
> + u8 control_data:4;
> + u8 version:3;
> + u8 sv:1;
> + u16 control_data_length:11;
> + u16 status:5;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> + u8 sv:1;
> + u8 version:3;
> + u8 control_data:4;
> + u16 status:5;
> + u16 control_data_length:11;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> + u64 stream_id;
> +} __packed;
> +
> +/* AVTP common stream data AVTPDU header format
> + * IEEE 1722#5.4
> + */
> +struct avtpdu_header {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + u8 subtype:7;
> + u8 cd:1;
> +
> + /* avtp_timestamp valid */
> + u8 tv: 1;
> +
> + /* gateway_info valid */
> + u8 gv:1;
> +
> + /* reserved */
> + u8 r:1;
> +
> + /*
> + * Media clock Restart toggle
> + */
> + u8 mr:1;
> +
> + u8 version:3;
> +
> + /* StreamID valid */
> + u8 sv:1;
> + u8 seqnr;
> +
> + /* Timestamp uncertain */
> + u8 tu:1;
> + u8 r2:7;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> +
> + u8 sv:1;
> + u8 version:3;
> + u8 mr:1;
> + u8 r:1;
> + u8 gv:1;
> + u8 tv: 1;
> +
> + u8 seqnr;
> + u8 r2:7;
> + u8 tu:1;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> +
> + u64 stream_id;
> +
> + u32 avtp_timestamp;
> + u32 gateway_info;
> +
> + /* Stream Data Length */
> + u16 sd_len;
> +
> + /* Protocol specific header, derived from avtp_subtype */
> + u16 psh;
> +
> + /* Stream Payload Data 0 to n octets
> + * n so that total size < MTU
> + */
> + u8 data[0];
> +} __packed;
> +
> +
> +/**
> + * struct tsn_list - The top level container of TSN
> + *
> + * This is what tsn_configfs refers to as 'tier-0'
> + *
> + * @head List of TSN cards
> + * @lock lock protecting global entries
> + * @tsn_subsys Ref to ConfigFS subsystem
> + *
> + * @running: hrtimer is running driving data out
> + * @tsn_timer: hrtimer container
> + * @num_avail Number of available TSN NICs exposed through ConfigFS
> + */
> +struct tsn_list {
> + struct list_head head;
> + struct mutex lock;
> + struct configfs_subsystem tsn_subsys;
> +
> + /*
> + * TSN-timer is running. Not to be confused with the per-link
> + * disabled flag which indicates if a remote client, like aplay,
> + * is pushing data to it.
> + */
> + atomic_t running;
> + struct hrtimer tsn_timer;
> + unsigned int period_ns;
> +
> +
> + size_t num_avail;
> +};
> +
> +/**
> + * struct tsn_nic
> + *
> + * Individual TSN-capable NICs, or 'tier-1' struct
> + *
> + * @list linked list of all TSN NICs
> + * @group configfs group
> + * @dev corresponding net_device
> + * @dma_size : size of the DMA buffer
> + * @dma_handle: housekeeping DMA-stuff
> + * @dma_mem : pointer to memory region we're using for DMAing to the NIC
> + * @name Name of NIC (same as name in dev), TO BE REMOVED
> + * @txq Size of Tx-queue. TO BE REMOVED
> + * @rx_registered flag indicating if a handler is registered for the nic
> + * @capable: if the NIC is capable for proper TSN traffic or if it must
> + * be emulated in software.
> + *
> + */
> +struct tsn_nic {
> + struct list_head list;
> + struct config_group group;
> + struct net_device *dev;
> + struct tsn_list *tsn_list;
> +
> + size_t dma_size;
> + dma_addr_t dma_handle;
> + void *dma_mem;
> +
> + char *name;
> + int txq;
> + u8 rx_registered:1;
> + u8 capable:1;
> + u8 reserved:6;
> +};
> +
> +struct tsn_shim_ops;
> +/**
> + * tsn_link - Structure describing a single TSN link
> + *
> + */
> +struct tsn_link {
> + /*
> + * Lock for protecting the buffer
> + */
> + spinlock_t lock;
> +
> + struct config_group group;
> + struct tsn_nic *nic;
> + struct hlist_node node;
> +
> + /* The link itself is active, and the tsn_core will treat it as
> + * an active participant and feed data from it to the
> + * network. This places some restrictions on which attributes
> + * can be changed.
> + *
> + * 1: active
> + * 0: inactive
> + */
> + atomic_t active;
> +
> + u64 timer_period_ns;
> +
> + /* Pointer to media-specific data.
> + * e.g. struct avb_chip
> + */
> + void *media_chip;
> +
> + u64 stream_id;
> +
> + /*
> + * The max required size for a _single_ TSN frame.
> + *
> + * To be used instead of channels and sample_freq.
> + */
> + u16 max_payload_size;
> + u16 shim_header_size;
> +
> + /*
> + * Size of buffer (in bytes) to use when handling data to/from
> + * NIC.
> + *
> + * Smaller size will result in client being called more often
> + * but also provides lower latencies.
> + */
> + size_t buffer_size;
> + size_t used_buffer_size;
> +
> + /*
> + * Used when frames are constructed and shipped to the network
> + * layer. If this is true, 0-frames will be sent insted of data
> + * from the buffer.
> + */
> + atomic_t buffer_active;
> +
> + /*
> + * ringbuffer for incoming or outging traffic
> + * +-----------------------------------+
> + * | ########## |
> + * +-----------------------------------+
> + * ^ ^ ^ ^
> + * buffer tail head end
> + *
> + * Buffer: start of memory area
> + * tail: first byte of data in buffer
> + * head: first unused slot in which to store new data
> + *
> + * head,tail is used to represent the position of 'live data' in
> + * the buffer.
> + */
> + void *buffer;
> + void *head;
> + void *tail;
> + void *end;
> +
> + /* Number of bytes to run refill/drain callbacks */
> + size_t low_water_mark;
> + size_t high_water_mark;
> +
> +
> + /*
> + * callback ops.
> + */
> + struct tsn_shim_ops *ops;
> +
> + /*
> + * EndStation Type
> + *
> + * Either Talker or Listener
> + *
> + * 1: We are *Talker*, i.e. producing data to send
> + * 0: We are *Listener*, i.e. we receive data from another ES.
> + *
> + * This is for a single link, so even though an end-station can
> + * be both Talker *and* Listener, a link can only be one.
> + */
> + u8 estype_talker;
> +
> + /*
> + * Link will use buffer managed by the shim. For this to work,
> + * the shim must:
> + *
> + * - call tsn_use_external_buffer(link, size);
> + * - provide tsn_shim_buffer_swap(link) in tsn_shim_ops
> + */
> + u8 external_buffer;
> +
> + u8 last_seqnr;
> +
> + /*
> + * Class can be either A or B
> + *
> + * ClassA: every 125us
> + * ClassB: every 250us
> + *
> + * This will also affect how large each frame will be.
> + */
> + u8 class_a:1;
> +
> + /*
> + * Any AVTP data stream must set the 802.1Q vlan id and priority
> + * Code point. This should be obtained from MSRP, default values
> + * are:
> + *
> + * pvid: SR_PVID 2
> + * pcp: Class A: 3
> + * Class B: 2
> + *
> + * See IEEE 802.1Q-2011, Sec 35.2.2.9.3 and table 6-6 in 6.6.2
> + * for details
> + */
> + u8 pcp_a:3;
> + u8 pcp_b:3;
> + u16 vlan_id:12;
> +
> + u8 remote_mac[6];
> +};
> +
> +/**
> + * tsn_link_on - make link active
> + *
> + * This cause most of the attributes to be treated read-only since we
> + * will have to re-negotiate with the network if most of these
> + * parameters change.
> + *
> + * Note: this means that the link will be handled by the rx-handler or
> + * the timer callback, but until the link_buffer is set active (via
> + * tsn_lb_on()), actual data is not moved.
> + *
> + * @link: link being set to active
> + */
> +static inline void tsn_link_on(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->active, 1);
> +}
> +
> +/**
> + * tsn_link_off - make link inactive
> + *
> + * The link will now be ignored by timer callback or the
> + * rx-handler. Attributes can be mostly freely changed (we assume that
> + * userspace sets values that are negotiated properly).
> + *
> + * @link: link to deactivate
> + */
> +static inline void tsn_link_off(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->active, 0);
> +}
> +
> +/**
> + * tsn_link_is_on - query link to see if it is active
> + *
> + * Mostly used by tsn_configfs to respect the "read-only" once link is
> + * configured and made active.
> + *
> + * @link active link
> + * @returns 1 if active/on, 0 otherwise
> + */
> +static inline int tsn_link_is_on(struct tsn_link *link)
> +{
> + if (link)
> + return atomic_read(&link->active);
> + return 0;
> +}
> +
> +/**
> + * tsn_set_buffer_size - adjust buffersize to match a shim
> + *
> + * This will not allocate (or deallcoate) memory, just adjust how much
> + * of the buffer allocated in tsn_prepare_link is being used. tsn_
> + * expects tsn_clear_buffer_size() to be invoked when stream is closed.
> + */
> +int tsn_set_buffer_size(struct tsn_link *link, size_t bsize);
> +int tsn_clear_buffer_size(struct tsn_link *link);
> +
> +/**
> + * tsn_buffer_write write data into the buffer from shim
> + *
> + * This is called from the shim-driver when more data is available and
> + * data needs to be pushed out to the network.
> + *
> + * NOTE: This is used when TSN handles the databuffer. This will not be
> + * needed for "shim-hosted" buffers.
> + *
> + * _If_ this function is called when the link is inactive, it will
> + * _enable_ the link (i.e. link will mark the buffer as 'active'). Do
> + * not copy data into the buffer unless you are ready to start sending
> + * frames!
> + *
> + * @link active link
> + * @src the buffer to copy data from
> + * @bytes bytes to copy
> + * @return bytes copied from link->buffer or negative error
> + */
> +int tsn_buffer_write(struct tsn_link *link, void *src, size_t bytes);
> +
> +
> +/**
> + * tsn_buffer_read - read data from link->buffer and give to shim
> + *
> + * When we act as a listener, this is what the shim (should|will) call
> + * to grab data. It typically grabs much more data than the _net
> + * equivalent. It also do not trigger a refill-event the same way
> + * buffer_read_net does.
> + *
> + * @param link current link that holds the buffer
> + * @param buffer the buffer to copy into, must be at least of size bytes
> + * @param bytes number of bytes.
> + *
> + * Note that this routine does NOT CARE about channels, samplesize etc,
> + * it is a _pure_ copy that handles ringbuffer wraps etc.
> + *
> + * This function have side-effects as it will update internal tsn_link
> + * values.
> + *
> + * @return Bytes copied into link->buffer, negative value upon error.
> + */
> +int tsn_buffer_read(struct tsn_link *link, void *buffer, size_t bytes);
> +
> +/**
> + * tsn_lb_enable - TSN Link Buffer Enable
> + *
> + * Mark the link as "buffer-enabled" which will let the core start
> + * shifting data in/out of the buffer instead of ignoring incoming
> + * frames or sending "nullframes".
> + *
> + * This is for the network-end of the tsn-buffer, i.e.
> + * - when enabled frames *from* the network will be inserted into the buffer,
> + * - or frames going *out* will include data from the buffer instead of sending
> + * null-frames.
> + *
> + * When disabled, data will be zero'd, e.g Tx will send NULL-frames and
> + * Rx will silently drop the frames.
> + *
> + * @link: active link
> + */
> +static inline void tsn_lb_enable(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->buffer_active, 1);
> +}
> +
> +/**
> + * tsn_lb_disable - stop using the buffer for the net-side of TSN
> + *
> + * When we close a stream, we do not necessarily tear down the link, and
> + * we need to handle the data in some way.
> + */
> +static inline void tsn_lb_disable(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->buffer_active, 0);
> +}
> +
> +/**
> + * tsn_lb() - query if we have disabled pushing of data to/from link-buffer
> + *
> + * @param struct tsn_link *link - active link
> + * @returns 1 if link is enabled
> + */
> +static inline int tsn_lb(struct tsn_link *link)
> +{
> + if (link)
> + return atomic_read(&link->buffer_active);
> +
> + /* if link is NULL; buffer not active */
> + return 0;
> +}
> +
> +
> +/**
> + * Shim ops - what tsn_core use when calling back into the shim. All ops
> + * must be reentrant.
> + */
> +#define SHIM_NAME_SIZE 32
> +struct tsn_shim_ops {
> +
> + /* internal linked list used by tsn_core to keep track of all
> + * shims.
> + */
> + struct list_head head;
> +
> + /**
> + * name - a unique name identifying this shim
> + *
> + * This is what userspace use to indicate to core what SHIM a
> + * particular link will use. If the name is already present,
> + * core will reject this name.
> + */
> + char shim_name[SHIM_NAME_SIZE];
> +
> + /**
> + * probe - callback when a new link of this type is instantiated.
> + *
> + * When a new link is brought online, this is called once the
> + * essential parts of tsn_core has finiesh. Once probe_cb has
> + * finisehd, the shim _must_ be ready to accept data to/from
> + * tsn_core. On the other hand, due to the final steps of setup,
> + * it cannot expect to be called into action immediately after
> + * probe has finished.
> + *
> + * In other words, shim must be ready, but core doesn't have to
> + *
> + * @param : a particular link to pass along to the probe-function.
> + */
> + int (*probe)(struct tsn_link *link);
> +
> + /**
> + * buffer_swap - set a new buffer for the link. [OPTIONAL]
> + *
> + * Used when external buffering is enabled.
> + *
> + * When called, a new buffer must be returned WITHOUT blocking
> + * as this will be called from interrupt context.
> + *
> + * The buffer returned from the shim must be at least the size
> + * of used_buffer_size.
> + *
> + * @param current link
> + * @param old_buffer the buffer that are no longer needed
> + * @param used number of bytes in buffer that has been filled with data.
> + * @return new buffer to use
> + */
> + void * (*buffer_swap)(struct tsn_link *link, void *old_buffer,
> + size_t used);
> +
> + /**
> + * buffer_refill - signal shim that more data is required
> + * @link Active link
> + *
> + * This function should not do anything that can preempt the
> + * task (kmalloc, sleeping lock) or invoke actions that can take
> + * a long time to complete.
> + *
> + * This will be called from tsn_buffer_read_net() when available
> + * data in the buffer drops below low_water_mark. It will be
> + * called with the link-lock *held*
> + */
> + size_t (*buffer_refill)(struct tsn_link *link);
> +
> + /**
> + * buffer_drain - shim need to copy data from buffer
> + *
> + * This will be called from tsn_buffer_write_net() when data in
> + * the buffer exceeds high_water_mark.
> + *
> + * The expected behavior is for the shim to then fill data into
> + * the buffer via tsn_buffer_write()
> + */
> + size_t (*buffer_drain)(struct tsn_link *link);
> +
> + /**
> + * media_close - shut down media controller properly
> + *
> + * when the link is closed/removed for some reason
> + * external to the media controller (ALSA soundcard, v4l2 driver
> + * etc), we call this to clean up.
> + *
> + * Normal operation is stopped before media_close is called, but
> + * all references should be valid. TSN core expects media_close
> + * to handle any local cleanup, once returned, any references in
> + * stale tsn_links cannot be trusted.
> + *
> + * @link: current link where data is stored
> + * @returns: 0 upon success, negative on error.
> + */
> + int (*media_close)(struct tsn_link *link);
> +
> + /**
> + * hdr_size - ask shim how large the header is
> + *
> + * Needed when reserving space in skb for transmitting data.
> + *
> + * @link: current link where data is stored
> + * @return: size of header for this shim
> + */
> + size_t (*hdr_size)(struct tsn_link *link);
> +
> + /**
> + * copy_size - ask client how much from the buffer to include in
> + * the next frame.
> + *
> + * This is for *outgoing* frames, incoming frames
> + * have 'sd_len' set in the header.
> + *
> + * Note: copy_size should not return a size larger
> + * than link->max_payload_size
> + */
> + size_t (*copy_size)(struct tsn_link *link);
> +
> + /**
> + * validate_header - let the shim validate subtype-header
> + *
> + * Both psh and data may (or may not) contain headers that need
> + * validating. This is the responsibility of the shim to
> + * validate, and ops->valdiate_header() will be called before
> + * any data is copied from the incoming frame and into the
> + * buffer.
> + *
> + * Important: tsn_core expects validate_header to _not_ alter
> + * the contents of the frame, and ideally, validate_header could
> + * be called multiple times and give the same result.
> + *
> + * @param: active link owning the new data
> + * @param: start of data-unit header
> + *
> + * This function will be called from interrupt-context and MUST
> + * NOT take any locks.
> + */
> + int (*validate_header)(struct tsn_link *link,
> + struct avtpdu_header *header);
> +
> + /**
> + * assemble_header - add shim-specific headers
> + *
> + * This adds the headers required by the current shim after the
> + * generic 1722-header.
> + *
> + * @param: active link
> + * @param: start of data-unit header
> + * @param: size of data to send in this frame
> + * @return void
> + */
> + void (*assemble_header)(struct tsn_link *link,
> + struct avtpdu_header *header, size_t bytes);
> +
> + /**
> + * get_payload_data - get a pointer to where the data is stored
> + *
> + * core will use the pointer (or drop it if NULL is returned)
> + * and copy header->sd_len bytes of *consecutive* data from the
> + * target memory and into the buffer memory.
> + *
> + * This is called with relevant locks held, from interrupt context.
> + *
> + * @param link active link
> + * @param header header of frame, which contains data
> + * @returns pointer to memory to copy from
> + */
> + void * (*get_payload_data)(struct tsn_link *link,
> + struct avtpdu_header *header);
> +};
> +/**
> + * tsn_shim_register_ops - register shim-callbacks for a given shim
> + *
> + * @param shim_ops - callbacks. The ops-struct should be kept intact for
> + * as long as the driver is running.
> + *
> + *
> + */
> +int tsn_shim_register_ops(struct tsn_shim_ops *shim_ops);
> +
> +/**
> + * tsn_shim_deregister_ops - remove callback for module
> + *
> + * Completely remove shim_ops. This will close any links currently using
> + * this shim. Note: the links will be closed, but _not_ removed.
> + *
> + * @param shim_ops ops associated with this shim
> + */
> +void tsn_shim_deregister_ops(struct tsn_shim_ops *shim_ops);
> +
> +/**
> + * tsn_shim_get_active : return the name of the currently loaded shim
> + *
> + * @param current link
> + * @return name of shim (matches an entry from exported triggers)
> + */
> +char *tsn_shim_get_active(struct tsn_link *link);
> +
> +/**
> + * tsn_shim_find_by_name find shim_ops by name
> + *
> + * @param name of shim
> + * @return shim or NULL if not found/error.
> + */
> +struct tsn_shim_ops *tsn_shim_find_by_name(const char *name);
> +
> +/**
> + * tsn_shim_export_probe_triggers - export a list of registered shims
> + *
> + * @param page to write content into
> + * @returns length of data written to page
> + */
> +ssize_t tsn_shim_export_probe_triggers(char *page);
> +
> +/**
> + * tsn_get_framesize - get the size of the next TSN frame to send
> + *
> + * This will call into the shim to get the next chunk of data to
> + * read. Some sanitychecking is performed, i.e.
> + *
> + * 0 <= size <= max_payload_size
> + *
> + * @param struct tsn_link *link active link
> + * @returns size of frame in bytes or negative on error.
> + */
> +static inline size_t tsn_shim_get_framesize(struct tsn_link *link)
> +{
> + size_t ret;
> +
> + ret = link->ops->copy_size(link);
> + if (ret <= link->max_payload_size)
> + return ret;
> + return link->max_payload_size;
> +}
> +
> +/**
> + * tsn_get_hdr_size - get the size of the shim-specific header size
> + *
> + * The shim will add it's own header to the frame.
> + */
> +static inline size_t tsn_shim_get_hdr_size(struct tsn_link *link)
> +{
> + size_t ret;
> +
> + if (!link || !link->ops->hdr_size)
> + return -EINVAL;
> + ret = link->ops->hdr_size(link);
> + if (ret > link->max_payload_size)
> + return -EINVAL;
> + return ret;
> +}
> +
> +#endif /* _TSN_H */
> --
> 2.7.4
>
--
Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 4/8] Add TSN header for the driver |
| Message-ID | <rJ02J-yf-5@gated-at.bofh.it> |
| In reply to | #1420077 |
Clearing up netdev-typo
-H
On Sun, Jun 12, 2016 at 12:22:17AM +0200, Henrik Austad wrote:
> From: Henrik Austad <haustad@cisco.com>
>
> This defines the general TSN headers for network packets, the
> shim-interface and the central 'tsn_list' structure.
>
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Henrik Austad <haustad@cisco.com>
> ---
> include/linux/tsn.h | 806 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 806 insertions(+)
> create mode 100644 include/linux/tsn.h
>
> diff --git a/include/linux/tsn.h b/include/linux/tsn.h
> new file mode 100644
> index 0000000..0e1f732b
> --- /dev/null
> +++ b/include/linux/tsn.h
> @@ -0,0 +1,806 @@
> +/* TSN - Time Sensitive Networking
> + *
> + * Copyright (C) 2016- Henrik Austad <haustad@cisco.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _TSN_H
> +#define _TSN_H
> +#include <linux/list.h>
> +#include <linux/configfs.h>
> +#include <linux/hrtimer.h>
> +
> +/* The naming here can be a bit confusing as we call it TSN but naming
> + * suggests 'AVB'. Reason: IEE 1722 was written before the working group
> + * was renamed to Time Sensitive Networking.
> + *
> + * To be precise. TSN describes the protocol for shipping data, AVB is a
> + * medialayer which you can build on top of TSN.
> + *
> + * For this reason the frames are given avb-names whereas the functions
> + * use tsn_-naming.
> + */
> +
> +/* 7 bit value 0x00 - 0x7F */
> +enum avtp_subtype {
> + AVTP_61883_IIDC = 0,
> + AVTP_MMA = 0x1,
> + AVTP_MAAP = 0x7e,
> + AVTP_EXPERIMENTAL = 0x7f,
> +};
> +
> +/* NOTE NOTE NOTE !!
> + * The headers below use bitfields extensively and verifications
> + * are needed when using little-endian vs big-endian systems.
> + */
> +
> +/* Common part of avtph header
> + *
> + * AVB Transport Protocol Common Header
> + *
> + * Defined in 1722-2011 Sec. 5.2
> + */
> +struct avtp_ch {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + /* use avtp_subtype enum.
> + */
> + u8 subtype:7;
> +
> + /* Controlframe: 1
> + * Dataframe : 0
> + */
> + u8 cd:1;
> +
> + /* Type specific data, part 1 */
> + u8 tsd_1:4;
> +
> + /* In current version of AVB, only 0 is valid, all other values
> + * are reserved for future versions.
> + */
> + u8 version:3;
> +
> + /* Valid StreamID in frame
> + *
> + * ControlData not related to a specific stream should clear
> + * this (and have stream_id = 0), _all_ other values should set
> + * this to 1.
> + */
> + u8 sv:1;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> + u8 sv:1;
> + u8 version:3;
> + u8 tsd_1:4;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> + /* Type specific data (adjacent to tsd_1, but split due to bitfield) */
> + u16 tsd_2;
> + u64 stream_id;
> +
> + /*
> + * payload by subtype
> + */
> + u8 pbs[0];
> +} __packed;
> +
> +/* AVTPDU Common Control header format
> + * IEEE 1722#5.3
> + */
> +struct avtpc_header {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + u8 subtype:7;
> + u8 cd:1;
> + u8 control_data:4;
> + u8 version:3;
> + u8 sv:1;
> + u16 control_data_length:11;
> + u16 status:5;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> + u8 sv:1;
> + u8 version:3;
> + u8 control_data:4;
> + u16 status:5;
> + u16 control_data_length:11;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> + u64 stream_id;
> +} __packed;
> +
> +/* AVTP common stream data AVTPDU header format
> + * IEEE 1722#5.4
> + */
> +struct avtpdu_header {
> +#if defined(__LITTLE_ENDIAN_BITFIELD)
> + u8 subtype:7;
> + u8 cd:1;
> +
> + /* avtp_timestamp valid */
> + u8 tv: 1;
> +
> + /* gateway_info valid */
> + u8 gv:1;
> +
> + /* reserved */
> + u8 r:1;
> +
> + /*
> + * Media clock Restart toggle
> + */
> + u8 mr:1;
> +
> + u8 version:3;
> +
> + /* StreamID valid */
> + u8 sv:1;
> + u8 seqnr;
> +
> + /* Timestamp uncertain */
> + u8 tu:1;
> + u8 r2:7;
> +#elif defined(__BIG_ENDIAN_BITFIELD)
> + u8 cd:1;
> + u8 subtype:7;
> +
> + u8 sv:1;
> + u8 version:3;
> + u8 mr:1;
> + u8 r:1;
> + u8 gv:1;
> + u8 tv: 1;
> +
> + u8 seqnr;
> + u8 r2:7;
> + u8 tu:1;
> +#else
> +#error "Unknown Endianness, cannot determine bitfield ordering"
> +#endif
> +
> + u64 stream_id;
> +
> + u32 avtp_timestamp;
> + u32 gateway_info;
> +
> + /* Stream Data Length */
> + u16 sd_len;
> +
> + /* Protocol specific header, derived from avtp_subtype */
> + u16 psh;
> +
> + /* Stream Payload Data 0 to n octets
> + * n so that total size < MTU
> + */
> + u8 data[0];
> +} __packed;
> +
> +
> +/**
> + * struct tsn_list - The top level container of TSN
> + *
> + * This is what tsn_configfs refers to as 'tier-0'
> + *
> + * @head List of TSN cards
> + * @lock lock protecting global entries
> + * @tsn_subsys Ref to ConfigFS subsystem
> + *
> + * @running: hrtimer is running driving data out
> + * @tsn_timer: hrtimer container
> + * @num_avail Number of available TSN NICs exposed through ConfigFS
> + */
> +struct tsn_list {
> + struct list_head head;
> + struct mutex lock;
> + struct configfs_subsystem tsn_subsys;
> +
> + /*
> + * TSN-timer is running. Not to be confused with the per-link
> + * disabled flag which indicates if a remote client, like aplay,
> + * is pushing data to it.
> + */
> + atomic_t running;
> + struct hrtimer tsn_timer;
> + unsigned int period_ns;
> +
> +
> + size_t num_avail;
> +};
> +
> +/**
> + * struct tsn_nic
> + *
> + * Individual TSN-capable NICs, or 'tier-1' struct
> + *
> + * @list linked list of all TSN NICs
> + * @group configfs group
> + * @dev corresponding net_device
> + * @dma_size : size of the DMA buffer
> + * @dma_handle: housekeeping DMA-stuff
> + * @dma_mem : pointer to memory region we're using for DMAing to the NIC
> + * @name Name of NIC (same as name in dev), TO BE REMOVED
> + * @txq Size of Tx-queue. TO BE REMOVED
> + * @rx_registered flag indicating if a handler is registered for the nic
> + * @capable: if the NIC is capable for proper TSN traffic or if it must
> + * be emulated in software.
> + *
> + */
> +struct tsn_nic {
> + struct list_head list;
> + struct config_group group;
> + struct net_device *dev;
> + struct tsn_list *tsn_list;
> +
> + size_t dma_size;
> + dma_addr_t dma_handle;
> + void *dma_mem;
> +
> + char *name;
> + int txq;
> + u8 rx_registered:1;
> + u8 capable:1;
> + u8 reserved:6;
> +};
> +
> +struct tsn_shim_ops;
> +/**
> + * tsn_link - Structure describing a single TSN link
> + *
> + */
> +struct tsn_link {
> + /*
> + * Lock for protecting the buffer
> + */
> + spinlock_t lock;
> +
> + struct config_group group;
> + struct tsn_nic *nic;
> + struct hlist_node node;
> +
> + /* The link itself is active, and the tsn_core will treat it as
> + * an active participant and feed data from it to the
> + * network. This places some restrictions on which attributes
> + * can be changed.
> + *
> + * 1: active
> + * 0: inactive
> + */
> + atomic_t active;
> +
> + u64 timer_period_ns;
> +
> + /* Pointer to media-specific data.
> + * e.g. struct avb_chip
> + */
> + void *media_chip;
> +
> + u64 stream_id;
> +
> + /*
> + * The max required size for a _single_ TSN frame.
> + *
> + * To be used instead of channels and sample_freq.
> + */
> + u16 max_payload_size;
> + u16 shim_header_size;
> +
> + /*
> + * Size of buffer (in bytes) to use when handling data to/from
> + * NIC.
> + *
> + * Smaller size will result in client being called more often
> + * but also provides lower latencies.
> + */
> + size_t buffer_size;
> + size_t used_buffer_size;
> +
> + /*
> + * Used when frames are constructed and shipped to the network
> + * layer. If this is true, 0-frames will be sent insted of data
> + * from the buffer.
> + */
> + atomic_t buffer_active;
> +
> + /*
> + * ringbuffer for incoming or outging traffic
> + * +-----------------------------------+
> + * | ########## |
> + * +-----------------------------------+
> + * ^ ^ ^ ^
> + * buffer tail head end
> + *
> + * Buffer: start of memory area
> + * tail: first byte of data in buffer
> + * head: first unused slot in which to store new data
> + *
> + * head,tail is used to represent the position of 'live data' in
> + * the buffer.
> + */
> + void *buffer;
> + void *head;
> + void *tail;
> + void *end;
> +
> + /* Number of bytes to run refill/drain callbacks */
> + size_t low_water_mark;
> + size_t high_water_mark;
> +
> +
> + /*
> + * callback ops.
> + */
> + struct tsn_shim_ops *ops;
> +
> + /*
> + * EndStation Type
> + *
> + * Either Talker or Listener
> + *
> + * 1: We are *Talker*, i.e. producing data to send
> + * 0: We are *Listener*, i.e. we receive data from another ES.
> + *
> + * This is for a single link, so even though an end-station can
> + * be both Talker *and* Listener, a link can only be one.
> + */
> + u8 estype_talker;
> +
> + /*
> + * Link will use buffer managed by the shim. For this to work,
> + * the shim must:
> + *
> + * - call tsn_use_external_buffer(link, size);
> + * - provide tsn_shim_buffer_swap(link) in tsn_shim_ops
> + */
> + u8 external_buffer;
> +
> + u8 last_seqnr;
> +
> + /*
> + * Class can be either A or B
> + *
> + * ClassA: every 125us
> + * ClassB: every 250us
> + *
> + * This will also affect how large each frame will be.
> + */
> + u8 class_a:1;
> +
> + /*
> + * Any AVTP data stream must set the 802.1Q vlan id and priority
> + * Code point. This should be obtained from MSRP, default values
> + * are:
> + *
> + * pvid: SR_PVID 2
> + * pcp: Class A: 3
> + * Class B: 2
> + *
> + * See IEEE 802.1Q-2011, Sec 35.2.2.9.3 and table 6-6 in 6.6.2
> + * for details
> + */
> + u8 pcp_a:3;
> + u8 pcp_b:3;
> + u16 vlan_id:12;
> +
> + u8 remote_mac[6];
> +};
> +
> +/**
> + * tsn_link_on - make link active
> + *
> + * This cause most of the attributes to be treated read-only since we
> + * will have to re-negotiate with the network if most of these
> + * parameters change.
> + *
> + * Note: this means that the link will be handled by the rx-handler or
> + * the timer callback, but until the link_buffer is set active (via
> + * tsn_lb_on()), actual data is not moved.
> + *
> + * @link: link being set to active
> + */
> +static inline void tsn_link_on(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->active, 1);
> +}
> +
> +/**
> + * tsn_link_off - make link inactive
> + *
> + * The link will now be ignored by timer callback or the
> + * rx-handler. Attributes can be mostly freely changed (we assume that
> + * userspace sets values that are negotiated properly).
> + *
> + * @link: link to deactivate
> + */
> +static inline void tsn_link_off(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->active, 0);
> +}
> +
> +/**
> + * tsn_link_is_on - query link to see if it is active
> + *
> + * Mostly used by tsn_configfs to respect the "read-only" once link is
> + * configured and made active.
> + *
> + * @link active link
> + * @returns 1 if active/on, 0 otherwise
> + */
> +static inline int tsn_link_is_on(struct tsn_link *link)
> +{
> + if (link)
> + return atomic_read(&link->active);
> + return 0;
> +}
> +
> +/**
> + * tsn_set_buffer_size - adjust buffersize to match a shim
> + *
> + * This will not allocate (or deallcoate) memory, just adjust how much
> + * of the buffer allocated in tsn_prepare_link is being used. tsn_
> + * expects tsn_clear_buffer_size() to be invoked when stream is closed.
> + */
> +int tsn_set_buffer_size(struct tsn_link *link, size_t bsize);
> +int tsn_clear_buffer_size(struct tsn_link *link);
> +
> +/**
> + * tsn_buffer_write write data into the buffer from shim
> + *
> + * This is called from the shim-driver when more data is available and
> + * data needs to be pushed out to the network.
> + *
> + * NOTE: This is used when TSN handles the databuffer. This will not be
> + * needed for "shim-hosted" buffers.
> + *
> + * _If_ this function is called when the link is inactive, it will
> + * _enable_ the link (i.e. link will mark the buffer as 'active'). Do
> + * not copy data into the buffer unless you are ready to start sending
> + * frames!
> + *
> + * @link active link
> + * @src the buffer to copy data from
> + * @bytes bytes to copy
> + * @return bytes copied from link->buffer or negative error
> + */
> +int tsn_buffer_write(struct tsn_link *link, void *src, size_t bytes);
> +
> +
> +/**
> + * tsn_buffer_read - read data from link->buffer and give to shim
> + *
> + * When we act as a listener, this is what the shim (should|will) call
> + * to grab data. It typically grabs much more data than the _net
> + * equivalent. It also do not trigger a refill-event the same way
> + * buffer_read_net does.
> + *
> + * @param link current link that holds the buffer
> + * @param buffer the buffer to copy into, must be at least of size bytes
> + * @param bytes number of bytes.
> + *
> + * Note that this routine does NOT CARE about channels, samplesize etc,
> + * it is a _pure_ copy that handles ringbuffer wraps etc.
> + *
> + * This function have side-effects as it will update internal tsn_link
> + * values.
> + *
> + * @return Bytes copied into link->buffer, negative value upon error.
> + */
> +int tsn_buffer_read(struct tsn_link *link, void *buffer, size_t bytes);
> +
> +/**
> + * tsn_lb_enable - TSN Link Buffer Enable
> + *
> + * Mark the link as "buffer-enabled" which will let the core start
> + * shifting data in/out of the buffer instead of ignoring incoming
> + * frames or sending "nullframes".
> + *
> + * This is for the network-end of the tsn-buffer, i.e.
> + * - when enabled frames *from* the network will be inserted into the buffer,
> + * - or frames going *out* will include data from the buffer instead of sending
> + * null-frames.
> + *
> + * When disabled, data will be zero'd, e.g Tx will send NULL-frames and
> + * Rx will silently drop the frames.
> + *
> + * @link: active link
> + */
> +static inline void tsn_lb_enable(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->buffer_active, 1);
> +}
> +
> +/**
> + * tsn_lb_disable - stop using the buffer for the net-side of TSN
> + *
> + * When we close a stream, we do not necessarily tear down the link, and
> + * we need to handle the data in some way.
> + */
> +static inline void tsn_lb_disable(struct tsn_link *link)
> +{
> + if (link)
> + atomic_set(&link->buffer_active, 0);
> +}
> +
> +/**
> + * tsn_lb() - query if we have disabled pushing of data to/from link-buffer
> + *
> + * @param struct tsn_link *link - active link
> + * @returns 1 if link is enabled
> + */
> +static inline int tsn_lb(struct tsn_link *link)
> +{
> + if (link)
> + return atomic_read(&link->buffer_active);
> +
> + /* if link is NULL; buffer not active */
> + return 0;
> +}
> +
> +
> +/**
> + * Shim ops - what tsn_core use when calling back into the shim. All ops
> + * must be reentrant.
> + */
> +#define SHIM_NAME_SIZE 32
> +struct tsn_shim_ops {
> +
> + /* internal linked list used by tsn_core to keep track of all
> + * shims.
> + */
> + struct list_head head;
> +
> + /**
> + * name - a unique name identifying this shim
> + *
> + * This is what userspace use to indicate to core what SHIM a
> + * particular link will use. If the name is already present,
> + * core will reject this name.
> + */
> + char shim_name[SHIM_NAME_SIZE];
> +
> + /**
> + * probe - callback when a new link of this type is instantiated.
> + *
> + * When a new link is brought online, this is called once the
> + * essential parts of tsn_core has finiesh. Once probe_cb has
> + * finisehd, the shim _must_ be ready to accept data to/from
> + * tsn_core. On the other hand, due to the final steps of setup,
> + * it cannot expect to be called into action immediately after
> + * probe has finished.
> + *
> + * In other words, shim must be ready, but core doesn't have to
> + *
> + * @param : a particular link to pass along to the probe-function.
> + */
> + int (*probe)(struct tsn_link *link);
> +
> + /**
> + * buffer_swap - set a new buffer for the link. [OPTIONAL]
> + *
> + * Used when external buffering is enabled.
> + *
> + * When called, a new buffer must be returned WITHOUT blocking
> + * as this will be called from interrupt context.
> + *
> + * The buffer returned from the shim must be at least the size
> + * of used_buffer_size.
> + *
> + * @param current link
> + * @param old_buffer the buffer that are no longer needed
> + * @param used number of bytes in buffer that has been filled with data.
> + * @return new buffer to use
> + */
> + void * (*buffer_swap)(struct tsn_link *link, void *old_buffer,
> + size_t used);
> +
> + /**
> + * buffer_refill - signal shim that more data is required
> + * @link Active link
> + *
> + * This function should not do anything that can preempt the
> + * task (kmalloc, sleeping lock) or invoke actions that can take
> + * a long time to complete.
> + *
> + * This will be called from tsn_buffer_read_net() when available
> + * data in the buffer drops below low_water_mark. It will be
> + * called with the link-lock *held*
> + */
> + size_t (*buffer_refill)(struct tsn_link *link);
> +
> + /**
> + * buffer_drain - shim need to copy data from buffer
> + *
> + * This will be called from tsn_buffer_write_net() when data in
> + * the buffer exceeds high_water_mark.
> + *
> + * The expected behavior is for the shim to then fill data into
> + * the buffer via tsn_buffer_write()
> + */
> + size_t (*buffer_drain)(struct tsn_link *link);
> +
> + /**
> + * media_close - shut down media controller properly
> + *
> + * when the link is closed/removed for some reason
> + * external to the media controller (ALSA soundcard, v4l2 driver
> + * etc), we call this to clean up.
> + *
> + * Normal operation is stopped before media_close is called, but
> + * all references should be valid. TSN core expects media_close
> + * to handle any local cleanup, once returned, any references in
> + * stale tsn_links cannot be trusted.
> + *
> + * @link: current link where data is stored
> + * @returns: 0 upon success, negative on error.
> + */
> + int (*media_close)(struct tsn_link *link);
> +
> + /**
> + * hdr_size - ask shim how large the header is
> + *
> + * Needed when reserving space in skb for transmitting data.
> + *
> + * @link: current link where data is stored
> + * @return: size of header for this shim
> + */
> + size_t (*hdr_size)(struct tsn_link *link);
> +
> + /**
> + * copy_size - ask client how much from the buffer to include in
> + * the next frame.
> + *
> + * This is for *outgoing* frames, incoming frames
> + * have 'sd_len' set in the header.
> + *
> + * Note: copy_size should not return a size larger
> + * than link->max_payload_size
> + */
> + size_t (*copy_size)(struct tsn_link *link);
> +
> + /**
> + * validate_header - let the shim validate subtype-header
> + *
> + * Both psh and data may (or may not) contain headers that need
> + * validating. This is the responsibility of the shim to
> + * validate, and ops->valdiate_header() will be called before
> + * any data is copied from the incoming frame and into the
> + * buffer.
> + *
> + * Important: tsn_core expects validate_header to _not_ alter
> + * the contents of the frame, and ideally, validate_header could
> + * be called multiple times and give the same result.
> + *
> + * @param: active link owning the new data
> + * @param: start of data-unit header
> + *
> + * This function will be called from interrupt-context and MUST
> + * NOT take any locks.
> + */
> + int (*validate_header)(struct tsn_link *link,
> + struct avtpdu_header *header);
> +
> + /**
> + * assemble_header - add shim-specific headers
> + *
> + * This adds the headers required by the current shim after the
> + * generic 1722-header.
> + *
> + * @param: active link
> + * @param: start of data-unit header
> + * @param: size of data to send in this frame
> + * @return void
> + */
> + void (*assemble_header)(struct tsn_link *link,
> + struct avtpdu_header *header, size_t bytes);
> +
> + /**
> + * get_payload_data - get a pointer to where the data is stored
> + *
> + * core will use the pointer (or drop it if NULL is returned)
> + * and copy header->sd_len bytes of *consecutive* data from the
> + * target memory and into the buffer memory.
> + *
> + * This is called with relevant locks held, from interrupt context.
> + *
> + * @param link active link
> + * @param header header of frame, which contains data
> + * @returns pointer to memory to copy from
> + */
> + void * (*get_payload_data)(struct tsn_link *link,
> + struct avtpdu_header *header);
> +};
> +/**
> + * tsn_shim_register_ops - register shim-callbacks for a given shim
> + *
> + * @param shim_ops - callbacks. The ops-struct should be kept intact for
> + * as long as the driver is running.
> + *
> + *
> + */
> +int tsn_shim_register_ops(struct tsn_shim_ops *shim_ops);
> +
> +/**
> + * tsn_shim_deregister_ops - remove callback for module
> + *
> + * Completely remove shim_ops. This will close any links currently using
> + * this shim. Note: the links will be closed, but _not_ removed.
> + *
> + * @param shim_ops ops associated with this shim
> + */
> +void tsn_shim_deregister_ops(struct tsn_shim_ops *shim_ops);
> +
> +/**
> + * tsn_shim_get_active : return the name of the currently loaded shim
> + *
> + * @param current link
> + * @return name of shim (matches an entry from exported triggers)
> + */
> +char *tsn_shim_get_active(struct tsn_link *link);
> +
> +/**
> + * tsn_shim_find_by_name find shim_ops by name
> + *
> + * @param name of shim
> + * @return shim or NULL if not found/error.
> + */
> +struct tsn_shim_ops *tsn_shim_find_by_name(const char *name);
> +
> +/**
> + * tsn_shim_export_probe_triggers - export a list of registered shims
> + *
> + * @param page to write content into
> + * @returns length of data written to page
> + */
> +ssize_t tsn_shim_export_probe_triggers(char *page);
> +
> +/**
> + * tsn_get_framesize - get the size of the next TSN frame to send
> + *
> + * This will call into the shim to get the next chunk of data to
> + * read. Some sanitychecking is performed, i.e.
> + *
> + * 0 <= size <= max_payload_size
> + *
> + * @param struct tsn_link *link active link
> + * @returns size of frame in bytes or negative on error.
> + */
> +static inline size_t tsn_shim_get_framesize(struct tsn_link *link)
> +{
> + size_t ret;
> +
> + ret = link->ops->copy_size(link);
> + if (ret <= link->max_payload_size)
> + return ret;
> + return link->max_payload_size;
> +}
> +
> +/**
> + * tsn_get_hdr_size - get the size of the shim-specific header size
> + *
> + * The shim will add it's own header to the frame.
> + */
> +static inline size_t tsn_shim_get_hdr_size(struct tsn_link *link)
> +{
> + size_t ret;
> +
> + if (!link || !link->ops->hdr_size)
> + return -EINVAL;
> + ret = link->ops->hdr_size(link);
> + if (ret > link->max_payload_size)
> + return -EINVAL;
> + return ret;
> +}
> +
> +#endif /* _TSN_H */
> --
> 2.7.4
>
--
Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 00:40 +0200 |
| Subject | [very-RFC 3/8] Adding TSN-driver to Intel I210 controller |
| Message-ID | <rIZJn-rN-23@gated-at.bofh.it> |
| In reply to | #1420070 |
This adds support for loading the igb.ko module with tsn
capabilities. This requires a 2-step approach. First enabling TSN in
.config, then load the module with use_tsn=1.
Once enabled and loaded, the controller will be placed in "Qav-mode"
which is when the credit-based shaper is available, 3 of the queues are
removed from regular traffic, max payload is set to 1522 octets (no
jumboframes allowed).
It dumps the registers of interest before and after, so this clutters
kern.log a bit. In time this will be reduced / tied to the debug-param
for the module.
Note: currently this driver is *not* stable, it is still a work in
progress.
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Cc: intel-wired-lan@lists.osuosl.org
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Henrik Austad <haustad@cisco.com>
---
drivers/net/ethernet/intel/Kconfig | 18 ++
drivers/net/ethernet/intel/igb/Makefile | 2 +-
drivers/net/ethernet/intel/igb/igb.h | 19 ++
drivers/net/ethernet/intel/igb/igb_main.c | 10 +-
drivers/net/ethernet/intel/igb/igb_tsn.c | 396 ++++++++++++++++++++++++++++++
5 files changed, 443 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/intel/igb/igb_tsn.c
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 714bd10..8e620a9 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -99,6 +99,24 @@ config IGB
To compile this driver as a module, choose M here. The module
will be called igb.
+config IGB_TSN
+ tristate "TSN Support for Intel(R) 82575/82576 i210 Network Controller"
+ depends on IGB && TSN
+ ---help---
+ This driver supports TSN (AVB) on Intel I210 network controllers.
+
+ When enabled, it will allow the module to be loaded with
+ "use_tsn" which will initialize the controller to A/V-mode
+ instead of legacy-mode. This will take 3 of the tx-queues and
+ place them in 802.1Q QoS mode and enable the credit-based
+ shaper for 2 of the queues.
+
+ If built with this option, but not loaded with use_tsn, the
+ only difference is a slightly larger module, no extra
+ code paths are called.
+
+ If unsure, say No
+
config IGB_HWMON
bool "Intel(R) PCI-Express Gigabit adapters HWMON support"
default y
diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
index 5bcb2de..1a9b776 100644
--- a/drivers/net/ethernet/intel/igb/Makefile
+++ b/drivers/net/ethernet/intel/igb/Makefile
@@ -33,4 +33,4 @@ obj-$(CONFIG_IGB) += igb.o
igb-objs := igb_main.o igb_ethtool.o e1000_82575.o \
e1000_mac.o e1000_nvm.o e1000_phy.o e1000_mbx.o \
- e1000_i210.o igb_ptp.o igb_hwmon.o
+ e1000_i210.o igb_ptp.o igb_hwmon.o igb_tsn.o
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index b9609af..708f705 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -356,6 +356,7 @@ struct hwmon_buff {
#define IGB_RETA_SIZE 128
/* board specific private data structure */
+
struct igb_adapter {
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
@@ -472,6 +473,13 @@ struct igb_adapter {
int copper_tries;
struct e1000_info ei;
u16 eee_advert;
+
+#if IS_ENABLED(CONFIG_IGB_TSN)
+ /* Reserved BW for class A and B */
+ u16 sra_idleslope_res;
+ u16 srb_idleslope_res;
+ u8 tsn_ready:1;
+#endif /* IGB_TSN */
};
#define IGB_FLAG_HAS_MSI BIT(0)
@@ -552,6 +560,17 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va,
struct sk_buff *skb);
int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
+/* This should be the only place where we add ifdeffery
+ * to include tsn-stuff or not. Everything else is located in igb_tsn.c
+ */
+#if IS_ENABLED(CONFIG_IGB_TSN)
+void igb_tsn_init(struct igb_adapter *adapter);
+int igb_tsn_capable(struct net_device *netdev);
+int igb_tsn_link_configure(struct net_device *netdev, enum sr_class sr_class,
+ u16 framesize, u16 vid);
+#else
+static inline void igb_tsn_init(struct igb_adapter *adapter) { }
+#endif /* CONFIG_IGB_TSN */
void igb_set_flag_queue_pairs(struct igb_adapter *, const u32);
#ifdef CONFIG_IGB_HWMON
void igb_sysfs_exit(struct igb_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ef3d642..4d8789f 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -2142,6 +2142,10 @@ static const struct net_device_ops igb_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = igb_netpoll,
#endif
+#if IS_ENABLED(CONFIG_IGB_TSN)
+ .ndo_tsn_capable = igb_tsn_capable,
+ .ndo_tsn_link_configure = igb_tsn_link_configure,
+#endif /* CONFIG_IGB_TSN */
.ndo_fix_features = igb_fix_features,
.ndo_set_features = igb_set_features,
.ndo_fdb_add = igb_ndo_fdb_add,
@@ -2665,6 +2669,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* do hw tstamp init after resetting */
igb_ptp_init(adapter);
+ igb_tsn_init(adapter);
+
dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
/* print bus type/speed/width info, not applicable to i354 */
if (hw->mac.type != e1000_i354) {
@@ -5323,8 +5329,10 @@ static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,
/* The minimum packet size with TCTL.PSP set is 17 so pad the skb
* in order to meet this minimum size requirement.
*/
- if (skb_put_padto(skb, 17))
+ if (skb_put_padto(skb, 17)) {
+ pr_err("%s: skb_put_padto FAILED. skb->len < 17\n", __func__);
return NETDEV_TX_OK;
+ }
return igb_xmit_frame_ring(skb, igb_tx_queue_mapping(adapter, skb));
}
diff --git a/drivers/net/ethernet/intel/igb/igb_tsn.c b/drivers/net/ethernet/intel/igb/igb_tsn.c
new file mode 100644
index 0000000..641f4f2
--- /dev/null
+++ b/drivers/net/ethernet/intel/igb/igb_tsn.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright(c) 2015-2016 Henrik Austad <haustad@cisco.com>
+ * Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/* FIXME: This should probably be handled by some Makefile-magic */
+
+#if IS_ENABLED(CONFIG_IGB_TSN)
+#include "igb.h"
+#include <linux/module.h>
+
+/* NOTE: keep the defines not present in e1000_regs.h to avoid
+ * cluttering too many files. Once we are pretty stable, these will move
+ * into it's proper home. Until then, make merge a bit easier by
+ * avoiding it
+ */
+
+/* Qav regs */
+#define E1000_IRPBS 0x02404 /* Rx Packet Buffer Size - RW */
+#define E1000_ITPBS 0x03404 /* Tx buffer size assignment */
+#define E1000_TQAVCTRL 0x03570 /* Tx Qav Control */
+#define E1000_DTXMXPKTSZ 0x0355C /* DMA TX Maximum Packet Size */
+
+/* Qav defines. */
+#define E1000_TQAVCH_ZERO_CREDIT 0x80000000
+#define E1000_LINK_RATE 0x7735
+
+/* queue mode, 0=strict, 1=SR mode */
+#define E1000_TQAVCC_QUEUEMODE 0x80000000
+/* Transmit mode, 0=legacy, 1=QAV */
+#define E1000_TQAVCTRL_TXMODE 0x00000001
+/* report DMA time of tx packets */
+#define E1000_TQAVCTRL_1588_STAT_EN 0x00000004
+/* data fetch arbitration */
+#define E1000_TQAVCTRL_DATA_FETCH_ARB 0x00000010
+/* data tx arbitration */
+#define E1000_TQAVCTRL_DATA_TRAN_ARB 0x00000100
+/* data launch time valid */
+#define E1000_TQAVCTRL_DATA_TRAN_TIM 0x00000200
+/* stall SP to guarantee SR */
+#define E1000_TQAVCTRL_SP_WAIT_SR 0x00000400
+
+/* ... and associated shift value */
+#define E1000_TQAVCTRL_FETCH_TM_SHIFT (16)
+
+/* QAV Tx mode control registers where _n can be 0 or 1. */
+#define E1000_TQAVCC(_idx) (0x03004 + 0x40 * (_idx))
+
+/* Tx Qav High Credit - See 7.2.7.6 for calculations
+ * intel 8.12.18
+ */
+#define E1000_TQAVHC(_idx) (0x0300C + 0x40 * (_idx))
+
+/* Queues priority masks where _n and _p can be 0-3. */
+
+#define MAX_FRAME_SIZE 1522
+#define MIN_FRAME_SIZE 64
+
+static int use_tsn = -1;
+static int debug_tsn = -1;
+module_param(use_tsn, int, 0);
+module_param(debug_tsn, int, 0);
+MODULE_PARM_DESC(use_tsn, "use_tsn (0=off, 1=enabled)");
+MODULE_PARM_DESC(debug_tsn, "debug_tsn (0=off, 1=enabled)");
+
+/* For a full list of the registers dumped here, see sec 8.1.3 in the
+ * i210 controller datasheet.
+ */
+static inline void _tsn_dump_regs(struct igb_adapter *adapter)
+{
+ u32 val = 0;
+ struct device *dev;
+ struct e1000_hw *hw = &adapter->hw;
+
+ /* do not dump regs if we're not debugging driver */
+ if (debug_tsn != 1)
+ return;
+
+ dev = &adapter->pdev->dev;
+ dev_info(dev, "num_tx_queues=%d, num_rx_queues=%d\n",
+ adapter->num_tx_queues, adapter->num_rx_queues);
+
+ /* 0x0008 - E1000_STATUS Device status register */
+ val = rd32(E1000_STATUS);
+ dev_info(&adapter->pdev->dev, "\n");
+ dev_info(dev, "Status: FullDuplex=%s, LinkUp=%s, speed=%0x01x\n",
+ val & 0x1 ? "FD" : "HD",
+ val & 0x2 ? "LU" : "LD",
+ val & 0xc0 >> 6);
+
+ /* E1000_VET vlan ether type */
+ val = rd32(E1000_VET);
+ dev_info(dev, "VLAN ether type: VET.VET=0x%04x, VET.VET_EXT=0x%04x\n",
+ val & 0xffff, (val >> 16) & 0xffff);
+
+ /* E1000_RXPBS (RXPBSIZE) Rx Packet Buffer Size */
+ val = rd32(E1000_RXPBS);
+ dev_info(dev, "Rx Packet buffer: RXPBSIZE=%dkB, Bmc2ospbsize=%dkB, cfg_ts_en=%s\n",
+ val & 0x1f,
+ (val >> 6) & 0x1f,
+ (val & (1 << 31)) ? "cfg_ts_en" : "cfg_ts_dis");
+
+ /* Transmit stuff */
+ /* E1000_TXPBS (TXPBSIZE) Tx Packet Buffer Size - RW */
+ val = rd32(E1000_TXPBS);
+ dev_info(dev, "Tx Packet buffer: Txpb0size=%dkB, Txpb1size=%dkB, Txpb2size=%dkB, Txpb3size=%dkB, os2Bmcpbsize=%dkB\n",
+ val & 0x3f, (val >> 6) & 0x3f, (val >> 12) & 0x3f,
+ (val >> 18) & 0x3f, (val >> 24) & 0x3f);
+
+ /* E1000_TCTL (TCTL) Tx control - RW*/
+ val = rd32(E1000_TCTL);
+ dev_info(dev, "Tx control reg: TxEnable=%s, CT=0x%X\n",
+ val & 2 ? "EN" : "DIS", (val >> 3) & 0x3F);
+
+ /* TQAVHC : Transmit Qav High credits 0x300C + 0x40*n - RW */
+ val = rd32(E1000_TQAVHC(0));
+ dev_info(dev, "E1000_TQAVHC0: %0x08x\n", val);
+ val = rd32(E1000_TQAVHC(1));
+ dev_info(dev, "E1000_TQAVHC1: %0x08x\n", val);
+
+ /* TQAVCC[0-1]: Transmit Qav 0x3004 + 0x40*n - RW */
+ val = rd32(E1000_TQAVCC(0));
+ dev_info(dev, "E1000_TQAVCC0: idleSlope=%02x, QueueMode=%s\n",
+ val % 0xff,
+ val > 31 ? "Stream reservation" : "Strict priority");
+ val = rd32(E1000_TQAVCC(1));
+ dev_info(dev, "E1000_TQAVCC1: idleSlope=%02x, QueueMode=%s\n",
+ val % 0xff,
+ val > 31 ? "Stream reservation" : "Strict priority");
+
+ /* TQAVCTRL : Transmit Qav control - RW */
+ val = rd32(E1000_TQAVCTRL);
+ dev_info(dev, "E1000_TQAVCTRL: TransmitMode=%s,1588_STAT_EN=%s,DataFetchARB=%s,DataTranARB=%s,DataTranTIM=%s,SP_WAIT_SR=%s,FetchTimDelta=%dns (0x%04x)\n",
+ (val & 0x0001) ? "Qav" : "Legacy",
+ (val & 0x0004) ? "En" : "Dis",
+ (val & 0x0010) ? "Most Empty" : "Round Robin",
+ (val & 0x0100) ? "Credit Shaper" : "Strict priority",
+ (val & 0x0200) ? "Valid" : "N/A",
+ (val & 0x0400) ? "Wait" : "nowait",
+ (val >> 16) * 32, (val >> 16));
+}
+
+/* Place the NIC in Qav-mode.
+ *
+ * This will result in a _single_ queue for normal BE traffic, the rest
+ * will be grabbed by the Qav-machinery and kept for strict priority
+ * transmission.
+ *
+ * I210 Datasheet Sec 7.2.7.7 gives a lot of information.
+ */
+void igb_tsn_init(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 val;
+
+ if (use_tsn != 1) {
+ adapter->tsn_ready = 0;
+ dev_info(&adapter->pdev->dev, "%s got use_tsn > 0 (%d)\n",
+ __func__, use_tsn);
+ return;
+ }
+
+ if (debug_tsn < 0 || debug_tsn > 1)
+ debug_tsn = 0;
+
+ if (!adapter->pdev) {
+ adapter->tsn_ready = 0;
+ return;
+ }
+
+ switch (adapter->pdev->device) {
+ case 0x1533: /* E1000_DEV_ID_I210_COPPER */
+ case 0x1536: /* E1000_DEV_ID_I210_FIBER */
+ case 0x1537: /* E1000_DEV_ID_I210_SERDES: */
+ case 0x1538: /* E1000_DEV_ID_I210_SGMII: */
+ case 0x157b: /* E1000_DEV_ID_I210_COPPER_FLASHLESS: */
+ case 0x157c: /* E1000_DEV_ID_I210_SERDES_FLASHLESS: */
+ break;
+ default:
+ /* not a known IGB-TSN capable device */
+ adapter->tsn_ready = 0;
+ return;
+ }
+ _tsn_dump_regs(adapter);
+
+ /* Set Tx packet buffer size assignment, see 7.2.7.7 in i210
+ * PB0: 8kB
+ * PB1: 8kB
+ * PB2: 4kB
+ * PB3: 4kB
+ * os2bmcsize: 2kB
+ * sumTx: 26kB
+ *
+ * Rxpbsize: 0x20 (32kB)
+ * bmc2ossize: 0x02
+ * sumRx: 34kB
+ *
+ * See 8.3.1 && 8.3.2
+ */
+ val = (0x02 << 24 | 0x04 << 18 | 0x04 << 12 | 0x08 << 6 | 0x08);
+ wr32(E1000_ITPBS, val);
+ wr32(E1000_IRPBS, (0x02 << 6 | 0x20));
+
+ /* DMA Tx maximum packet size, the largest frame DMA should transport
+ * do not allow frames larger than 1522 + preample. Reg expects
+ * size in 64B increments. 802.1BA 6.3
+ * Round up to 1536 to handle 64B increments
+ *
+ * Initial value: 0x98 (152 => 9728 bytes)
+ */
+ wr32(E1000_DTXMXPKTSZ, 1536 >> 6);
+
+ /* Place card in Qav-mode, use tx-queue 0,1 for Qav
+ * (Credit-based shaper), 2,3 for standard priority (and
+ * best-effort) traffic.
+ *
+ * i210 8.12.19 and 8.12.21
+ *
+ * - Fetch: most empty and time based (not round-robin)
+ * - Transmit: Credit based shaper for SR queues
+ * - Data launch time valid (in Qav mode)
+ * - Wait for SR queues to ensure that launch time is always valid.
+ * - Set ~10us wait-time-delta, 32ns granularity
+ *
+ * Do *not* enable Tx for shaper (E1000_TQAVCTRL_DATA_TRAN_ARB)
+ * yet as we do not have data to Tx
+ */
+ val = E1000_TQAVCTRL_TXMODE |
+ E1000_TQAVCTRL_DATA_FETCH_ARB |
+ E1000_TQAVCTRL_DATA_TRAN_TIM |
+ E1000_TQAVCTRL_SP_WAIT_SR |
+ 320 << E1000_TQAVCTRL_FETCH_TM_SHIFT;
+
+ wr32(E1000_TQAVCTRL, val);
+
+ /* For now, only set CreditBased shaper for A and B, not set
+ * idleSlope as we have not yet gotten any streams.
+ * 8.12.19
+ */
+ wr32(E1000_TQAVCC(0), E1000_TQAVCC_QUEUEMODE);
+ wr32(E1000_TQAVCC(1), E1000_TQAVCC_QUEUEMODE);
+
+ wr32(E1000_TQAVHC(0), E1000_TQAVCH_ZERO_CREDIT);
+ wr32(E1000_TQAVHC(1), E1000_TQAVCH_ZERO_CREDIT);
+
+ /* reset Tx Descriptor tail and head for the queues */
+ wr32(E1000_TDT(0), 0);
+ wr32(E1000_TDT(1), 0);
+ wr32(E1000_TDH(0), 0);
+ wr32(E1000_TDH(1), 0);
+
+ _tsn_dump_regs(adapter);
+ dev_info(&adapter->pdev->dev, "\n");
+
+ adapter->sra_idleslope_res = 0;
+ adapter->srb_idleslope_res = 0;
+ adapter->tsn_ready = 1;
+
+ dev_info(&adapter->pdev->dev, "%s: setup done\n", __func__);
+}
+
+int igb_tsn_capable(struct net_device *netdev)
+{
+ struct igb_adapter *adapter;
+
+ if (!netdev)
+ return -EINVAL;
+ adapter = netdev_priv(netdev);
+ if (use_tsn == 1)
+ return adapter->tsn_ready == 1;
+ return 0;
+}
+
+/* igb_tsn_link_configure - configure NIC to handle a new stream
+ *
+ * @netdev: pointer to NIC device
+ * @class: the class for the stream used to find the correct queue.
+ * @framesize: size of each frame, *including* headers (not preamble)
+ * @vid: VLAN ID
+ *
+ * NOTE: the sr_class only instructs the driver which queue to use, not
+ * what priority the network expects for a given class. This is
+ * something userspace must find out and then let the tsn-driver set in
+ * the frame before xmit.
+ *
+ * FIXME: remove bw-req from a stream that goes away.
+ */
+int igb_tsn_link_configure(struct net_device *netdev, enum sr_class class,
+ u16 framesize, u16 vid)
+{
+ /* FIXME: push into adapter-storage */
+ static int class_a_size;
+ static int class_b_size;
+ int err;
+ u32 idle_slope_a = 0;
+ u32 idle_slope_b = 0;
+ u32 new_is = 0;
+ u32 hicred_a = 0;
+ u32 hicred_b = 0;
+ u32 tqavctrl;
+
+ struct igb_adapter *adapter;
+ struct e1000_hw *hw;
+
+ if (!netdev)
+ return -EINVAL;
+ adapter = netdev_priv(netdev);
+ hw = &adapter->hw;
+
+ if (!igb_tsn_capable(netdev)) {
+ pr_err("%s: NIC not capable\n", __func__);
+ return -EINVAL;
+ }
+
+ if (framesize > MAX_FRAME_SIZE || framesize < MIN_FRAME_SIZE) {
+ pr_err("%s: framesize (%u) must be [%d,%d]\n", __func__,
+ framesize, MIN_FRAME_SIZE, MAX_FRAME_SIZE);
+ return -EINVAL;
+ }
+
+ /* TODO: is this the correct place/way? Is it required? */
+ rtnl_lock();
+ pr_info("%s: adding VLAN %u to HW filter on device %s\n",
+ __func__, vid, netdev->name);
+ err = vlan_vid_add(netdev, htons(ETH_P_8021Q), vid);
+ if (err != 0)
+ pr_err("%s: error adding vlan %u, res=%d\n",
+ __func__, vid, err);
+ rtnl_unlock();
+
+ /* Grab current values of idle_slope */
+ idle_slope_a = rd32(E1000_TQAVHC(0)) & ~E1000_TQAVCH_ZERO_CREDIT;
+ idle_slope_b = rd32(E1000_TQAVHC(1)) & ~E1000_TQAVCH_ZERO_CREDIT;
+
+ /* Calculate new idle slope and add to appropriate idle_slope
+ * idle_slope = BW * linkrate * 2 (0r 0.2 for 100Mbit)
+ * BW: % of total bandwidth
+ */
+ new_is = framesize * E1000_LINK_RATE * 16 / 1000000;
+
+ switch (class) {
+ case SR_CLASS_A:
+ new_is *= 2; /* A is 8kHz, B is 4kHz */
+ idle_slope_a += new_is;
+ class_a_size = framesize;
+ break;
+ case SR_CLASS_B:
+ idle_slope_b += new_is;
+ class_b_size = framesize;
+ break;
+ default:
+ pr_err("%s: unhandled SR-class (%d)\n", __func__, class);
+ return -EINVAL;
+ }
+
+ /* HiCred: cred obtained while waiting for current frame &&
+ * higher-class frames to finish xmit.
+ *
+ * Covered in detail in 7.2.7.6 in i210 datasheet
+ * For class A: only worst-case framesize that just started;
+ * i.e. 1522 * idleSlope / linkrate;
+ * For class B: (worst-case framesize + burstSize(A))*idleSlope
+ *
+ * See 802.1Q Annex L, eq L.10 for hicred_a and L.41 for
+ * hicred_b
+ */
+ if (class == SR_CLASS_A) {
+ hicred_a = E1000_TQAVCH_ZERO_CREDIT + idle_slope_a * MAX_FRAME_SIZE / E1000_LINK_RATE;
+ wr32(E1000_TQAVCC(0), E1000_TQAVCC_QUEUEMODE | idle_slope_a);
+ wr32(E1000_TQAVHC(0), hicred_a);
+ } else {
+ hicred_b = E1000_TQAVCH_ZERO_CREDIT | idle_slope_b * (MAX_FRAME_SIZE + class_a_size) / (E1000_LINK_RATE - idle_slope_a);
+ wr32(E1000_TQAVCC(1), E1000_TQAVCC_QUEUEMODE | idle_slope_b);
+ wr32(E1000_TQAVHC(1), hicred_b);
+ }
+
+ /* Enable Tx for shaper now that we have data */
+ tqavctrl = rd32(E1000_TQAVCTRL);
+ if (!(tqavctrl & E1000_TQAVCTRL_DATA_TRAN_ARB)) {
+ tqavctrl |= E1000_TQAVCTRL_DATA_TRAN_ARB;
+ wr32(E1000_TQAVCTRL, tqavctrl);
+ }
+ _tsn_dump_regs(netdev_priv(netdev));
+ return 0;
+}
+
+#endif /* #if IS_ENABLED(CONFIG_IGB_TSN) */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Subject | Re: [very-RFC 3/8] Adding TSN-driver to Intel I210 controller |
| Message-ID | <rJ02K-yf-19@gated-at.bofh.it> |
| In reply to | #1420079 |
clearing up netdev-typo
-H
On Sun, Jun 12, 2016 at 12:22:16AM +0200, Henrik Austad wrote:
> This adds support for loading the igb.ko module with tsn
> capabilities. This requires a 2-step approach. First enabling TSN in
> .config, then load the module with use_tsn=1.
>
> Once enabled and loaded, the controller will be placed in "Qav-mode"
> which is when the credit-based shaper is available, 3 of the queues are
> removed from regular traffic, max payload is set to 1522 octets (no
> jumboframes allowed).
>
> It dumps the registers of interest before and after, so this clutters
> kern.log a bit. In time this will be reduced / tied to the debug-param
> for the module.
>
> Note: currently this driver is *not* stable, it is still a work in
> progress.
>
> Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
> Cc: intel-wired-lan@lists.osuosl.org
> Cc: "David S. Miller" <davem@davemloft.net>
> Signed-off-by: Henrik Austad <haustad@cisco.com>
> ---
> drivers/net/ethernet/intel/Kconfig | 18 ++
> drivers/net/ethernet/intel/igb/Makefile | 2 +-
> drivers/net/ethernet/intel/igb/igb.h | 19 ++
> drivers/net/ethernet/intel/igb/igb_main.c | 10 +-
> drivers/net/ethernet/intel/igb/igb_tsn.c | 396 ++++++++++++++++++++++++++++++
> 5 files changed, 443 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/ethernet/intel/igb/igb_tsn.c
>
> diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
> index 714bd10..8e620a9 100644
> --- a/drivers/net/ethernet/intel/Kconfig
> +++ b/drivers/net/ethernet/intel/Kconfig
> @@ -99,6 +99,24 @@ config IGB
> To compile this driver as a module, choose M here. The module
> will be called igb.
>
> +config IGB_TSN
> + tristate "TSN Support for Intel(R) 82575/82576 i210 Network Controller"
> + depends on IGB && TSN
> + ---help---
> + This driver supports TSN (AVB) on Intel I210 network controllers.
> +
> + When enabled, it will allow the module to be loaded with
> + "use_tsn" which will initialize the controller to A/V-mode
> + instead of legacy-mode. This will take 3 of the tx-queues and
> + place them in 802.1Q QoS mode and enable the credit-based
> + shaper for 2 of the queues.
> +
> + If built with this option, but not loaded with use_tsn, the
> + only difference is a slightly larger module, no extra
> + code paths are called.
> +
> + If unsure, say No
> +
> config IGB_HWMON
> bool "Intel(R) PCI-Express Gigabit adapters HWMON support"
> default y
> diff --git a/drivers/net/ethernet/intel/igb/Makefile b/drivers/net/ethernet/intel/igb/Makefile
> index 5bcb2de..1a9b776 100644
> --- a/drivers/net/ethernet/intel/igb/Makefile
> +++ b/drivers/net/ethernet/intel/igb/Makefile
> @@ -33,4 +33,4 @@ obj-$(CONFIG_IGB) += igb.o
>
> igb-objs := igb_main.o igb_ethtool.o e1000_82575.o \
> e1000_mac.o e1000_nvm.o e1000_phy.o e1000_mbx.o \
> - e1000_i210.o igb_ptp.o igb_hwmon.o
> + e1000_i210.o igb_ptp.o igb_hwmon.o igb_tsn.o
> diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
> index b9609af..708f705 100644
> --- a/drivers/net/ethernet/intel/igb/igb.h
> +++ b/drivers/net/ethernet/intel/igb/igb.h
> @@ -356,6 +356,7 @@ struct hwmon_buff {
> #define IGB_RETA_SIZE 128
>
> /* board specific private data structure */
> +
> struct igb_adapter {
> unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
>
> @@ -472,6 +473,13 @@ struct igb_adapter {
> int copper_tries;
> struct e1000_info ei;
> u16 eee_advert;
> +
> +#if IS_ENABLED(CONFIG_IGB_TSN)
> + /* Reserved BW for class A and B */
> + u16 sra_idleslope_res;
> + u16 srb_idleslope_res;
> + u8 tsn_ready:1;
> +#endif /* IGB_TSN */
> };
>
> #define IGB_FLAG_HAS_MSI BIT(0)
> @@ -552,6 +560,17 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va,
> struct sk_buff *skb);
> int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
> int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
> +/* This should be the only place where we add ifdeffery
> + * to include tsn-stuff or not. Everything else is located in igb_tsn.c
> + */
> +#if IS_ENABLED(CONFIG_IGB_TSN)
> +void igb_tsn_init(struct igb_adapter *adapter);
> +int igb_tsn_capable(struct net_device *netdev);
> +int igb_tsn_link_configure(struct net_device *netdev, enum sr_class sr_class,
> + u16 framesize, u16 vid);
> +#else
> +static inline void igb_tsn_init(struct igb_adapter *adapter) { }
> +#endif /* CONFIG_IGB_TSN */
> void igb_set_flag_queue_pairs(struct igb_adapter *, const u32);
> #ifdef CONFIG_IGB_HWMON
> void igb_sysfs_exit(struct igb_adapter *adapter);
> diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
> index ef3d642..4d8789f 100644
> --- a/drivers/net/ethernet/intel/igb/igb_main.c
> +++ b/drivers/net/ethernet/intel/igb/igb_main.c
> @@ -2142,6 +2142,10 @@ static const struct net_device_ops igb_netdev_ops = {
> #ifdef CONFIG_NET_POLL_CONTROLLER
> .ndo_poll_controller = igb_netpoll,
> #endif
> +#if IS_ENABLED(CONFIG_IGB_TSN)
> + .ndo_tsn_capable = igb_tsn_capable,
> + .ndo_tsn_link_configure = igb_tsn_link_configure,
> +#endif /* CONFIG_IGB_TSN */
> .ndo_fix_features = igb_fix_features,
> .ndo_set_features = igb_set_features,
> .ndo_fdb_add = igb_ndo_fdb_add,
> @@ -2665,6 +2669,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> /* do hw tstamp init after resetting */
> igb_ptp_init(adapter);
>
> + igb_tsn_init(adapter);
> +
> dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
> /* print bus type/speed/width info, not applicable to i354 */
> if (hw->mac.type != e1000_i354) {
> @@ -5323,8 +5329,10 @@ static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,
> /* The minimum packet size with TCTL.PSP set is 17 so pad the skb
> * in order to meet this minimum size requirement.
> */
> - if (skb_put_padto(skb, 17))
> + if (skb_put_padto(skb, 17)) {
> + pr_err("%s: skb_put_padto FAILED. skb->len < 17\n", __func__);
> return NETDEV_TX_OK;
> + }
>
> return igb_xmit_frame_ring(skb, igb_tx_queue_mapping(adapter, skb));
> }
> diff --git a/drivers/net/ethernet/intel/igb/igb_tsn.c b/drivers/net/ethernet/intel/igb/igb_tsn.c
> new file mode 100644
> index 0000000..641f4f2
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/igb/igb_tsn.c
> @@ -0,0 +1,396 @@
> +/*
> + * Copyright(c) 2015-2016 Henrik Austad <haustad@cisco.com>
> + * Cisco Systems, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +/* FIXME: This should probably be handled by some Makefile-magic */
> +
> +#if IS_ENABLED(CONFIG_IGB_TSN)
> +#include "igb.h"
> +#include <linux/module.h>
> +
> +/* NOTE: keep the defines not present in e1000_regs.h to avoid
> + * cluttering too many files. Once we are pretty stable, these will move
> + * into it's proper home. Until then, make merge a bit easier by
> + * avoiding it
> + */
> +
> +/* Qav regs */
> +#define E1000_IRPBS 0x02404 /* Rx Packet Buffer Size - RW */
> +#define E1000_ITPBS 0x03404 /* Tx buffer size assignment */
> +#define E1000_TQAVCTRL 0x03570 /* Tx Qav Control */
> +#define E1000_DTXMXPKTSZ 0x0355C /* DMA TX Maximum Packet Size */
> +
> +/* Qav defines. */
> +#define E1000_TQAVCH_ZERO_CREDIT 0x80000000
> +#define E1000_LINK_RATE 0x7735
> +
> +/* queue mode, 0=strict, 1=SR mode */
> +#define E1000_TQAVCC_QUEUEMODE 0x80000000
> +/* Transmit mode, 0=legacy, 1=QAV */
> +#define E1000_TQAVCTRL_TXMODE 0x00000001
> +/* report DMA time of tx packets */
> +#define E1000_TQAVCTRL_1588_STAT_EN 0x00000004
> +/* data fetch arbitration */
> +#define E1000_TQAVCTRL_DATA_FETCH_ARB 0x00000010
> +/* data tx arbitration */
> +#define E1000_TQAVCTRL_DATA_TRAN_ARB 0x00000100
> +/* data launch time valid */
> +#define E1000_TQAVCTRL_DATA_TRAN_TIM 0x00000200
> +/* stall SP to guarantee SR */
> +#define E1000_TQAVCTRL_SP_WAIT_SR 0x00000400
> +
> +/* ... and associated shift value */
> +#define E1000_TQAVCTRL_FETCH_TM_SHIFT (16)
> +
> +/* QAV Tx mode control registers where _n can be 0 or 1. */
> +#define E1000_TQAVCC(_idx) (0x03004 + 0x40 * (_idx))
> +
> +/* Tx Qav High Credit - See 7.2.7.6 for calculations
> + * intel 8.12.18
> + */
> +#define E1000_TQAVHC(_idx) (0x0300C + 0x40 * (_idx))
> +
> +/* Queues priority masks where _n and _p can be 0-3. */
> +
> +#define MAX_FRAME_SIZE 1522
> +#define MIN_FRAME_SIZE 64
> +
> +static int use_tsn = -1;
> +static int debug_tsn = -1;
> +module_param(use_tsn, int, 0);
> +module_param(debug_tsn, int, 0);
> +MODULE_PARM_DESC(use_tsn, "use_tsn (0=off, 1=enabled)");
> +MODULE_PARM_DESC(debug_tsn, "debug_tsn (0=off, 1=enabled)");
> +
> +/* For a full list of the registers dumped here, see sec 8.1.3 in the
> + * i210 controller datasheet.
> + */
> +static inline void _tsn_dump_regs(struct igb_adapter *adapter)
> +{
> + u32 val = 0;
> + struct device *dev;
> + struct e1000_hw *hw = &adapter->hw;
> +
> + /* do not dump regs if we're not debugging driver */
> + if (debug_tsn != 1)
> + return;
> +
> + dev = &adapter->pdev->dev;
> + dev_info(dev, "num_tx_queues=%d, num_rx_queues=%d\n",
> + adapter->num_tx_queues, adapter->num_rx_queues);
> +
> + /* 0x0008 - E1000_STATUS Device status register */
> + val = rd32(E1000_STATUS);
> + dev_info(&adapter->pdev->dev, "\n");
> + dev_info(dev, "Status: FullDuplex=%s, LinkUp=%s, speed=%0x01x\n",
> + val & 0x1 ? "FD" : "HD",
> + val & 0x2 ? "LU" : "LD",
> + val & 0xc0 >> 6);
> +
> + /* E1000_VET vlan ether type */
> + val = rd32(E1000_VET);
> + dev_info(dev, "VLAN ether type: VET.VET=0x%04x, VET.VET_EXT=0x%04x\n",
> + val & 0xffff, (val >> 16) & 0xffff);
> +
> + /* E1000_RXPBS (RXPBSIZE) Rx Packet Buffer Size */
> + val = rd32(E1000_RXPBS);
> + dev_info(dev, "Rx Packet buffer: RXPBSIZE=%dkB, Bmc2ospbsize=%dkB, cfg_ts_en=%s\n",
> + val & 0x1f,
> + (val >> 6) & 0x1f,
> + (val & (1 << 31)) ? "cfg_ts_en" : "cfg_ts_dis");
> +
> + /* Transmit stuff */
> + /* E1000_TXPBS (TXPBSIZE) Tx Packet Buffer Size - RW */
> + val = rd32(E1000_TXPBS);
> + dev_info(dev, "Tx Packet buffer: Txpb0size=%dkB, Txpb1size=%dkB, Txpb2size=%dkB, Txpb3size=%dkB, os2Bmcpbsize=%dkB\n",
> + val & 0x3f, (val >> 6) & 0x3f, (val >> 12) & 0x3f,
> + (val >> 18) & 0x3f, (val >> 24) & 0x3f);
> +
> + /* E1000_TCTL (TCTL) Tx control - RW*/
> + val = rd32(E1000_TCTL);
> + dev_info(dev, "Tx control reg: TxEnable=%s, CT=0x%X\n",
> + val & 2 ? "EN" : "DIS", (val >> 3) & 0x3F);
> +
> + /* TQAVHC : Transmit Qav High credits 0x300C + 0x40*n - RW */
> + val = rd32(E1000_TQAVHC(0));
> + dev_info(dev, "E1000_TQAVHC0: %0x08x\n", val);
> + val = rd32(E1000_TQAVHC(1));
> + dev_info(dev, "E1000_TQAVHC1: %0x08x\n", val);
> +
> + /* TQAVCC[0-1]: Transmit Qav 0x3004 + 0x40*n - RW */
> + val = rd32(E1000_TQAVCC(0));
> + dev_info(dev, "E1000_TQAVCC0: idleSlope=%02x, QueueMode=%s\n",
> + val % 0xff,
> + val > 31 ? "Stream reservation" : "Strict priority");
> + val = rd32(E1000_TQAVCC(1));
> + dev_info(dev, "E1000_TQAVCC1: idleSlope=%02x, QueueMode=%s\n",
> + val % 0xff,
> + val > 31 ? "Stream reservation" : "Strict priority");
> +
> + /* TQAVCTRL : Transmit Qav control - RW */
> + val = rd32(E1000_TQAVCTRL);
> + dev_info(dev, "E1000_TQAVCTRL: TransmitMode=%s,1588_STAT_EN=%s,DataFetchARB=%s,DataTranARB=%s,DataTranTIM=%s,SP_WAIT_SR=%s,FetchTimDelta=%dns (0x%04x)\n",
> + (val & 0x0001) ? "Qav" : "Legacy",
> + (val & 0x0004) ? "En" : "Dis",
> + (val & 0x0010) ? "Most Empty" : "Round Robin",
> + (val & 0x0100) ? "Credit Shaper" : "Strict priority",
> + (val & 0x0200) ? "Valid" : "N/A",
> + (val & 0x0400) ? "Wait" : "nowait",
> + (val >> 16) * 32, (val >> 16));
> +}
> +
> +/* Place the NIC in Qav-mode.
> + *
> + * This will result in a _single_ queue for normal BE traffic, the rest
> + * will be grabbed by the Qav-machinery and kept for strict priority
> + * transmission.
> + *
> + * I210 Datasheet Sec 7.2.7.7 gives a lot of information.
> + */
> +void igb_tsn_init(struct igb_adapter *adapter)
> +{
> + struct e1000_hw *hw = &adapter->hw;
> + u32 val;
> +
> + if (use_tsn != 1) {
> + adapter->tsn_ready = 0;
> + dev_info(&adapter->pdev->dev, "%s got use_tsn > 0 (%d)\n",
> + __func__, use_tsn);
> + return;
> + }
> +
> + if (debug_tsn < 0 || debug_tsn > 1)
> + debug_tsn = 0;
> +
> + if (!adapter->pdev) {
> + adapter->tsn_ready = 0;
> + return;
> + }
> +
> + switch (adapter->pdev->device) {
> + case 0x1533: /* E1000_DEV_ID_I210_COPPER */
> + case 0x1536: /* E1000_DEV_ID_I210_FIBER */
> + case 0x1537: /* E1000_DEV_ID_I210_SERDES: */
> + case 0x1538: /* E1000_DEV_ID_I210_SGMII: */
> + case 0x157b: /* E1000_DEV_ID_I210_COPPER_FLASHLESS: */
> + case 0x157c: /* E1000_DEV_ID_I210_SERDES_FLASHLESS: */
> + break;
> + default:
> + /* not a known IGB-TSN capable device */
> + adapter->tsn_ready = 0;
> + return;
> + }
> + _tsn_dump_regs(adapter);
> +
> + /* Set Tx packet buffer size assignment, see 7.2.7.7 in i210
> + * PB0: 8kB
> + * PB1: 8kB
> + * PB2: 4kB
> + * PB3: 4kB
> + * os2bmcsize: 2kB
> + * sumTx: 26kB
> + *
> + * Rxpbsize: 0x20 (32kB)
> + * bmc2ossize: 0x02
> + * sumRx: 34kB
> + *
> + * See 8.3.1 && 8.3.2
> + */
> + val = (0x02 << 24 | 0x04 << 18 | 0x04 << 12 | 0x08 << 6 | 0x08);
> + wr32(E1000_ITPBS, val);
> + wr32(E1000_IRPBS, (0x02 << 6 | 0x20));
> +
> + /* DMA Tx maximum packet size, the largest frame DMA should transport
> + * do not allow frames larger than 1522 + preample. Reg expects
> + * size in 64B increments. 802.1BA 6.3
> + * Round up to 1536 to handle 64B increments
> + *
> + * Initial value: 0x98 (152 => 9728 bytes)
> + */
> + wr32(E1000_DTXMXPKTSZ, 1536 >> 6);
> +
> + /* Place card in Qav-mode, use tx-queue 0,1 for Qav
> + * (Credit-based shaper), 2,3 for standard priority (and
> + * best-effort) traffic.
> + *
> + * i210 8.12.19 and 8.12.21
> + *
> + * - Fetch: most empty and time based (not round-robin)
> + * - Transmit: Credit based shaper for SR queues
> + * - Data launch time valid (in Qav mode)
> + * - Wait for SR queues to ensure that launch time is always valid.
> + * - Set ~10us wait-time-delta, 32ns granularity
> + *
> + * Do *not* enable Tx for shaper (E1000_TQAVCTRL_DATA_TRAN_ARB)
> + * yet as we do not have data to Tx
> + */
> + val = E1000_TQAVCTRL_TXMODE |
> + E1000_TQAVCTRL_DATA_FETCH_ARB |
> + E1000_TQAVCTRL_DATA_TRAN_TIM |
> + E1000_TQAVCTRL_SP_WAIT_SR |
> + 320 << E1000_TQAVCTRL_FETCH_TM_SHIFT;
> +
> + wr32(E1000_TQAVCTRL, val);
> +
> + /* For now, only set CreditBased shaper for A and B, not set
> + * idleSlope as we have not yet gotten any streams.
> + * 8.12.19
> + */
> + wr32(E1000_TQAVCC(0), E1000_TQAVCC_QUEUEMODE);
> + wr32(E1000_TQAVCC(1), E1000_TQAVCC_QUEUEMODE);
> +
> + wr32(E1000_TQAVHC(0), E1000_TQAVCH_ZERO_CREDIT);
> + wr32(E1000_TQAVHC(1), E1000_TQAVCH_ZERO_CREDIT);
> +
> + /* reset Tx Descriptor tail and head for the queues */
> + wr32(E1000_TDT(0), 0);
> + wr32(E1000_TDT(1), 0);
> + wr32(E1000_TDH(0), 0);
> + wr32(E1000_TDH(1), 0);
> +
> + _tsn_dump_regs(adapter);
> + dev_info(&adapter->pdev->dev, "\n");
> +
> + adapter->sra_idleslope_res = 0;
> + adapter->srb_idleslope_res = 0;
> + adapter->tsn_ready = 1;
> +
> + dev_info(&adapter->pdev->dev, "%s: setup done\n", __func__);
> +}
> +
> +int igb_tsn_capable(struct net_device *netdev)
> +{
> + struct igb_adapter *adapter;
> +
> + if (!netdev)
> + return -EINVAL;
> + adapter = netdev_priv(netdev);
> + if (use_tsn == 1)
> + return adapter->tsn_ready == 1;
> + return 0;
> +}
> +
> +/* igb_tsn_link_configure - configure NIC to handle a new stream
> + *
> + * @netdev: pointer to NIC device
> + * @class: the class for the stream used to find the correct queue.
> + * @framesize: size of each frame, *including* headers (not preamble)
> + * @vid: VLAN ID
> + *
> + * NOTE: the sr_class only instructs the driver which queue to use, not
> + * what priority the network expects for a given class. This is
> + * something userspace must find out and then let the tsn-driver set in
> + * the frame before xmit.
> + *
> + * FIXME: remove bw-req from a stream that goes away.
> + */
> +int igb_tsn_link_configure(struct net_device *netdev, enum sr_class class,
> + u16 framesize, u16 vid)
> +{
> + /* FIXME: push into adapter-storage */
> + static int class_a_size;
> + static int class_b_size;
> + int err;
> + u32 idle_slope_a = 0;
> + u32 idle_slope_b = 0;
> + u32 new_is = 0;
> + u32 hicred_a = 0;
> + u32 hicred_b = 0;
> + u32 tqavctrl;
> +
> + struct igb_adapter *adapter;
> + struct e1000_hw *hw;
> +
> + if (!netdev)
> + return -EINVAL;
> + adapter = netdev_priv(netdev);
> + hw = &adapter->hw;
> +
> + if (!igb_tsn_capable(netdev)) {
> + pr_err("%s: NIC not capable\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (framesize > MAX_FRAME_SIZE || framesize < MIN_FRAME_SIZE) {
> + pr_err("%s: framesize (%u) must be [%d,%d]\n", __func__,
> + framesize, MIN_FRAME_SIZE, MAX_FRAME_SIZE);
> + return -EINVAL;
> + }
> +
> + /* TODO: is this the correct place/way? Is it required? */
> + rtnl_lock();
> + pr_info("%s: adding VLAN %u to HW filter on device %s\n",
> + __func__, vid, netdev->name);
> + err = vlan_vid_add(netdev, htons(ETH_P_8021Q), vid);
> + if (err != 0)
> + pr_err("%s: error adding vlan %u, res=%d\n",
> + __func__, vid, err);
> + rtnl_unlock();
> +
> + /* Grab current values of idle_slope */
> + idle_slope_a = rd32(E1000_TQAVHC(0)) & ~E1000_TQAVCH_ZERO_CREDIT;
> + idle_slope_b = rd32(E1000_TQAVHC(1)) & ~E1000_TQAVCH_ZERO_CREDIT;
> +
> + /* Calculate new idle slope and add to appropriate idle_slope
> + * idle_slope = BW * linkrate * 2 (0r 0.2 for 100Mbit)
> + * BW: % of total bandwidth
> + */
> + new_is = framesize * E1000_LINK_RATE * 16 / 1000000;
> +
> + switch (class) {
> + case SR_CLASS_A:
> + new_is *= 2; /* A is 8kHz, B is 4kHz */
> + idle_slope_a += new_is;
> + class_a_size = framesize;
> + break;
> + case SR_CLASS_B:
> + idle_slope_b += new_is;
> + class_b_size = framesize;
> + break;
> + default:
> + pr_err("%s: unhandled SR-class (%d)\n", __func__, class);
> + return -EINVAL;
> + }
> +
> + /* HiCred: cred obtained while waiting for current frame &&
> + * higher-class frames to finish xmit.
> + *
> + * Covered in detail in 7.2.7.6 in i210 datasheet
> + * For class A: only worst-case framesize that just started;
> + * i.e. 1522 * idleSlope / linkrate;
> + * For class B: (worst-case framesize + burstSize(A))*idleSlope
> + *
> + * See 802.1Q Annex L, eq L.10 for hicred_a and L.41 for
> + * hicred_b
> + */
> + if (class == SR_CLASS_A) {
> + hicred_a = E1000_TQAVCH_ZERO_CREDIT + idle_slope_a * MAX_FRAME_SIZE / E1000_LINK_RATE;
> + wr32(E1000_TQAVCC(0), E1000_TQAVCC_QUEUEMODE | idle_slope_a);
> + wr32(E1000_TQAVHC(0), hicred_a);
> + } else {
> + hicred_b = E1000_TQAVCH_ZERO_CREDIT | idle_slope_b * (MAX_FRAME_SIZE + class_a_size) / (E1000_LINK_RATE - idle_slope_a);
> + wr32(E1000_TQAVCC(1), E1000_TQAVCC_QUEUEMODE | idle_slope_b);
> + wr32(E1000_TQAVHC(1), hicred_b);
> + }
> +
> + /* Enable Tx for shaper now that we have data */
> + tqavctrl = rd32(E1000_TQAVCTRL);
> + if (!(tqavctrl & E1000_TQAVCTRL_DATA_TRAN_ARB)) {
> + tqavctrl |= E1000_TQAVCTRL_DATA_TRAN_ARB;
> + wr32(E1000_TQAVCTRL, tqavctrl);
> + }
> + _tsn_dump_regs(netdev_priv(netdev));
> + return 0;
> +}
> +
> +#endif /* #if IS_ENABLED(CONFIG_IGB_TSN) */
> --
> 2.7.4
>
--
Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Henrik Austad <henrik@austad.us> |
|---|---|
| Date | 2016-06-12 01:00 +0200 |
| Message-ID | <rJ02K-yf-17@gated-at.bofh.it> |
| In reply to | #1420070 |
On Sun, Jun 12, 2016 at 12:22:13AM +0200, Henrik Austad wrote: > Hi all Sorry.. I somehow managed to mess up the address to netdev, so if you feel like replying to this, use this as it has the correct netdev-address. again, sorry > (series based on v4.7-rc2) > > This is a *very* early RFC for a TSN-driver in the kernel. It has been > floating around in my repo for a while and I would appreciate some > feedback on the overall design to avoid doing some major blunders. > > TSN: Time Sensitive Networking, formely known as AVB (Audio/Video > Bridging). > > There are at least one AVB-driver (the AV-part of TSN) in the kernel > already, however this driver aims to solve a wider scope as TSN can do > much more than just audio. A very basic ALSA-driver is added to the end > that allows you to play music between 2 machines using aplay in one end > and arecord | aplay on the other (some fiddling required) We have plans > for doing the same for v4l2 eventually (but there are other fishes to > fry first). The same goes for a TSN_SOCK type approach as well. > > TSN is all about providing infrastructure. Allthough there are a few > very interesting uses for TSN (reliable, deterministic network for audio > and video), once you have that reliable link, you can do a lot more. > > Some notes on the design: > > The driver is directed via ConfigFS as we need userspace to handle > stream-reservation (MSRP), discovery and enumeration (IEEE 1722.1) and > whatever other management is needed. Once we have all the required > attributes, we can create link using mkdir, and use write() to set the > attributes. Once ready, specify the 'shim' (basically a thin wrapper > between TSN and another subsystem) and we start pushing out frames. > > The network part: it ties directly into the rx-handler for receive and > writes skb's using netdev_start_xmit(). This could probably be > improved. 2 new fields in netdev_ops have been introduced, and the Intel > igb-driver has been updated (as this is available as a PCI-e card). The > igb-driver works-ish > > > What remains > - tie to (g)PTP properly, currently using ktime_get() for presentation > time > - get time from shim into TSN and vice versa > - let shim create/manage buffer > > Henrik Austad (8): > TSN: add documentation > TSN: Add the standard formerly known as AVB to the kernel > Adding TSN-driver to Intel I210 controller > Add TSN header for the driver > Add TSN machinery to drive the traffic from a shim over the network > Add TSN event-tracing > AVB ALSA - Add ALSA shim for TSN > MAINTAINERS: add TSN/AVB-entries > > Documentation/TSN/tsn.txt | 147 +++++ > MAINTAINERS | 14 + > drivers/media/Kconfig | 15 + > drivers/media/Makefile | 3 +- > drivers/media/avb/Makefile | 5 + > drivers/media/avb/avb_alsa.c | 742 +++++++++++++++++++++++ > drivers/media/avb/tsn_iec61883.h | 124 ++++ > drivers/net/ethernet/intel/Kconfig | 18 + > drivers/net/ethernet/intel/igb/Makefile | 2 +- > drivers/net/ethernet/intel/igb/igb.h | 19 + > drivers/net/ethernet/intel/igb/igb_main.c | 10 +- > drivers/net/ethernet/intel/igb/igb_tsn.c | 396 ++++++++++++ > include/linux/netdevice.h | 32 + > include/linux/tsn.h | 806 ++++++++++++++++++++++++ > include/trace/events/tsn.h | 349 +++++++++++ > net/Kconfig | 1 + > net/Makefile | 1 + > net/tsn/Kconfig | 32 + > net/tsn/Makefile | 6 + > net/tsn/tsn_configfs.c | 623 +++++++++++++++++++ > net/tsn/tsn_core.c | 975 ++++++++++++++++++++++++++++++ > net/tsn/tsn_header.c | 203 +++++++ > net/tsn/tsn_internal.h | 383 ++++++++++++ > net/tsn/tsn_net.c | 403 ++++++++++++ > 24 files changed, 5306 insertions(+), 3 deletions(-) > create mode 100644 Documentation/TSN/tsn.txt > create mode 100644 drivers/media/avb/Makefile > create mode 100644 drivers/media/avb/avb_alsa.c > create mode 100644 drivers/media/avb/tsn_iec61883.h > create mode 100644 drivers/net/ethernet/intel/igb/igb_tsn.c > create mode 100644 include/linux/tsn.h > create mode 100644 include/trace/events/tsn.h > create mode 100644 net/tsn/Kconfig > create mode 100644 net/tsn/Makefile > create mode 100644 net/tsn/tsn_configfs.c > create mode 100644 net/tsn/tsn_core.c > create mode 100644 net/tsn/tsn_header.c > create mode 100644 net/tsn/tsn_internal.h > create mode 100644 net/tsn/tsn_net.c > > -- > 2.7.4 -- Henrik Austad
[toc] | [prev] | [next] | [standalone]
| From | Joe Perches <joe@perches.com> |
|---|---|
| Date | 2016-06-12 09:40 +0200 |
| Subject | Re: [very-RFC 5/8] Add TSN machinery to drive the traffic from a shim over the network |
| Message-ID | <rJ89Y-5KR-15@gated-at.bofh.it> |
| In reply to | #1420070 |
On Sun, 2016-06-12 at 00:22 +0200, Henrik Austad wrote:
> From: Henrik Austad <haustad@cisco.com>
>
> In short summary:
>
> * tsn_core.c is the main driver of tsn, all new links go through
> here and all data to/form the shims are handled here
> core also manages the shim-interface.
[]
> diff --git a/net/tsn/tsn_configfs.c b/net/tsn/tsn_configfs.c
[]
> +static inline struct tsn_link *to_tsn_link(struct config_item *item)
> +{
> + /* this line causes checkpatch to WARN. making checkpatch happy,
> + * makes code messy..
> + */
> + return item ? container_of(to_config_group(item), struct tsn_link, group) : NULL;
> +}
How about
static inline struct tsn_link *to_tsn_link(struct config_item *item)
{
if (!item)
return NULL;
return container_of(to_config_group(item), struct tsn_link, group);
}
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web