Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1543643 > unrolled thread

[TSN RFC v2 0/9] TSN driver for the kernel

Started byhenrik@austad.us
First post2016-12-16 19:10 +0100
Last post2016-12-17 21:10 +0100
Articles 13 — 5 participants

Back to article view | Back to linux.kernel


Contents

  [TSN RFC v2 0/9] TSN driver for the kernel henrik@austad.us - 2016-12-16 19:10 +0100
    [TSN RFC v2 4/9] Adding TSN-driver to Intel I210 controller henrik@austad.us - 2016-12-16 19:10 +0100
    [TSN RFC v2 1/9] igb: add missing fields to TXDCTL-register henrik@austad.us - 2016-12-16 19:10 +0100
    [TSN RFC v2 8/9] AVB ALSA - Add ALSA shim for TSN henrik@austad.us - 2016-12-16 19:10 +0100
    [TSN RFC v2 5/9] Add TSN header for the driver henrik@austad.us - 2016-12-16 19:10 +0100
      Re: [TSN RFC v2 5/9] Add TSN header for the driver Richard Cochran <richardcochran@gmail.com> - 2016-12-16 23:20 +0100
        Re: [TSN RFC v2 5/9] Add TSN header for the driver Henrik Austad <henrik@austad.us> - 2016-12-17 10:00 +0100
    Re: [TSN RFC v2 0/9] TSN driver for the kernel Greg <gvrose8192@gmail.com> - 2016-12-16 19:20 +0100
      Re: [TSN RFC v2 0/9] TSN driver for the kernel David Miller <davem@davemloft.net> - 2016-12-16 19:30 +0100
        Re: [TSN RFC v2 0/9] TSN driver for the kernel Henrik Austad <henrik@austad.us> - 2016-12-16 20:10 +0100
    Re: [TSN RFC v2 0/9] TSN driver for the kernel Richard Cochran <richardcochran@gmail.com> - 2016-12-16 23:10 +0100
      Re: [TSN RFC v2 0/9] TSN driver for the kernel Henrik Austad <henrik@austad.us> - 2016-12-17 10:10 +0100
        Re: [TSN RFC v2 0/9] TSN driver for the kernel Richard Cochran <richardcochran@gmail.com> - 2016-12-17 21:10 +0100

#1543643 — [TSN RFC v2 0/9] TSN driver for the kernel

Fromhenrik@austad.us
Date2016-12-16 19:10 +0100
Subject[TSN RFC v2 0/9] TSN driver for the kernel
Message-ID<sP57b-75S-5@gated-at.bofh.it>
From: Henrik Austad <haustad@cisco.com>


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. This also includes running an
appropriate PTP daemon (TSN favors gPTP).

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 dev_queue_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 an AVB-capable NIC which is
available as a PCI-e card).

What remains (tl;dr: a lot) a.k.a "Known problems" or "working on it!"
- tie to (g)PTP properly, currently using ktime_get() for presentation
  time
- get time from shim into TSN
- let shim create/manage buffer
- redo parts of the link-stuff using RCUs, the current setup is a bit
  clumsy.
- The igb-driver does not work properly when compiled with IGB_TSN, some
  details in setting the register values needs to be figured out. I am
  working on this, but as it stands, the best bet is to load tsn using
  in_debug=1 to bypass the capability-check. I have had e1000 and sky2
  running for several days without crashing, igb crashes and burns
  violently.
- The ALSA driver does not handle multiple devices very well and is a
  work in progress.

* v2: changes since v1

Changes since v1
- updated to latest upstream kernel (v4.8)
- set dedicated enabled-attribute and let shim be stored in own (support
  future plan for enabling per-shim attributes)
- fixed endianess issue in bitfields used in tsn-structs
- Updated some of the trace-events to use trace_class
- Fix various silly typos
- Handle disabling of link from hrtimer a bit more gracefully (that
  actually works-ish).
- use old skb and size of skb when that is set (Reporte by Nikita)
- Move PCP-codes to NIC and not in the link itself
- Allow TSN-capable card to be loaded even when in debug-mode (and do
  not enforce TSN behaviour)
- Start hooking into ALSA's get_time_info hooks (very much incomplete)
- use threads for sending frames, wake from hrtimer-callback.
  This also queues up awaiting timers if we fail to complete the
  transmit before another timer arrives, it will immediately execute
  another iteration, so no events should be lost. That being said,
  should this happen, it is a clear bug as we really should complete
  well before the next interval.
- Cleanup link-locking and differentiate between Talker and Listener (as
  Listener grab link-lock from IRQ context)
- Change list-lock to spinlock as we may need to take a link-lock whilst
  holding the master list-lock.
- Do a more careful dance holding the spinlocks to regions only doing
  actual update.

Network driver (I210 only)
- bring up all Tx-/Rx-queues when igb is in TSN-mode regardless of how
  many CPUs the system has for I210
- Correctly calculate the idle_slope in I210's configure hook
- Update igb-driver with queue-select and return correct queue when
  sending TSN-frames
- add IGB_FLAG_QAV_PRIO flag to igb_adapter (to handle proper config of
  tx-ring when adapter is brought up.
- add TXDCTL logic (part of preparatory work for TSN) to igb-driver
- Improve SR(A|B) accountingo

ALSA Shim
- Allow userspace to grab much smaller chunks of data (down to a single
  Class A frame for S16_LE 2ch 48kHz).
- Create the card with index/id pattern to avoid collision with other
  cards.
* v1

Before reading on - this is not even beta, but I'd really appreciate if
people would comment on the overall architecture and perhaps provide
some pointers to where I should improve/fix/update
- thanks!

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.

There are at least one AVB-driver (the AV-part of TSN) in the kernel
already. 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.

Henrik Austad (9):
  igb: add missing fields to TXDCTL-register
  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                    |  345 ++++++++
 MAINTAINERS                                  |   14 +
 drivers/media/Kconfig                        |   15 +
 drivers/media/Makefile                       |    2 +-
 drivers/media/avb/Makefile                   |    5 +
 drivers/media/avb/avb_alsa.c                 |  793 +++++++++++++++++
 drivers/media/avb/tsn_iec61883.h             |  152 ++++
 drivers/net/ethernet/intel/Kconfig           |   18 +
 drivers/net/ethernet/intel/igb/Makefile      |    2 +-
 drivers/net/ethernet/intel/igb/e1000_82575.h |    4 +
 drivers/net/ethernet/intel/igb/igb.h         |   26 +
 drivers/net/ethernet/intel/igb/igb_main.c    |   39 +-
 drivers/net/ethernet/intel/igb/igb_tsn.c     |  468 ++++++++++
 include/linux/netdevice.h                    |   44 +
 include/linux/tsn.h                          |  952 +++++++++++++++++++++
 include/trace/events/tsn.h                   |  333 ++++++++
 net/Kconfig                                  |    1 +
 net/Makefile                                 |    1 +
 net/tsn/Kconfig                              |   32 +
 net/tsn/Makefile                             |    6 +
 net/tsn/tsn_configfs.c                       |  673 +++++++++++++++
 net/tsn/tsn_core.c                           | 1189 ++++++++++++++++++++++++++
 net/tsn/tsn_header.c                         |  162 ++++
 net/tsn/tsn_internal.h                       |  397 +++++++++
 net/tsn/tsn_net.c                            |  392 +++++++++
 25 files changed, 6061 insertions(+), 4 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]


#1543644 — [TSN RFC v2 4/9] Adding TSN-driver to Intel I210 controller

Fromhenrik@austad.us
Date2016-12-16 19:10 +0100
Subject[TSN RFC v2 4/9] Adding TSN-driver to Intel I210 controller
Message-ID<sP57c-75S-25@gated-at.bofh.it>
In reply to#1543643
From: Henrik Austad <henrik@austad.us>

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 if it is loaded with debug_tsn=1.

Regardless of number of online CPUs, it will enable *all* for Tx-queues as
2 is required for Qav traffic. This has not been tested extensively, so
there may be some instabilities in this.

Improved SR(A|B) accounting:
Use the idleslope-bins to keep track of how much time is reserved for
each class. This can then be used to strip the vlan-tag on the NIC when
the last stream goes (and also allow for reconfiguration of PCP when the
NIC is not sending TSN traffic).

Note: currently this driver is *not* stable, it is still a work in
progress, some points to keep tabs on:
- Set hicred to unlim (for testing this is ok and we avoid some nasty
  calculations)
- once we configure it for TSN, enable credit shaping, do not wait for
  first link to be configured (nobody else should use these queues after
  being configured).
- enable all Tx-/Rx-queues in TSN-mode regardless of num_online_cpus()
- Add 802.1Qav Prio-bit in adapter->flags
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Cc: Jesse Brandeburg <jesse.brandeburg@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      |  26 ++
 drivers/net/ethernet/intel/igb/igb_main.c |  39 ++-
 drivers/net/ethernet/intel/igb/igb_tsn.c  | 468 ++++++++++++++++++++++++++++++
 5 files changed, 550 insertions(+), 3 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 c0e1743..d4382b4 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 d11093d..474a5b4 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -394,6 +394,7 @@ struct igb_nfc_filter {
 };
 
 /* board specific private data structure */
+
 struct igb_adapter {
 	unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
 
@@ -519,6 +520,17 @@ struct igb_adapter {
 	/* lock for RX network flow classification filter */
 	spinlock_t nfc_lock;
 	bool etype_bitmap[MAX_ETYPE_FILTER];
+
+#if IS_ENABLED(CONFIG_IGB_TSN)
+	/* Reserved BW for class A and B */
+	s32 sra_idleslope_res;
+	s32 srb_idleslope_res;
+	u8 pcp_hi;
+	u8 pcp_lo;
+	u8 tsn_ready:1;
+	u8 tsn_vlan_added:1;
+	u8 res:6;
+#endif	/* IGB_TSN */
 };
 
 /* flags controlling PTP/1588 function */
@@ -540,6 +552,7 @@ struct igb_adapter {
 #define IGB_FLAG_HAS_MSIX		BIT(13)
 #define IGB_FLAG_EEE			BIT(14)
 #define IGB_FLAG_VLAN_PROMISC		BIT(15)
+#define IGB_FLAG_QAV_PRIO		BIT(16)
 
 /* Media Auto Sense */
 #define IGB_MAS_ENABLE_0		0X0001
@@ -603,6 +616,19 @@ 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, u8 add_link, u8 pcp_hi, u8 pcp_lo);
+u16 igb_tsn_select_queue(struct net_device *netdev, struct sk_buff *skb,
+			void *accel_priv, select_queue_fallback_t fallback);
+#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 9affd7c..0283864 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -256,6 +256,12 @@ static int debug = -1;
 module_param(debug, int, 0);
 MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
 
+static int use_tsn = 0;
+#if IS_ENABLED(CONFIG_IGB_TSN)
+MODULE_PARM_DESC(use_tsn, "use_tsn (0=off, 1=enabled)");
+module_param(use_tsn, int, 0);
+#endif
+
 struct igb_reg_info {
 	u32 ofs;
 	char *name;
@@ -675,6 +681,9 @@ static int __init igb_init_module(void)
 {
 	int ret;
 
+	if (use_tsn != 1)
+		use_tsn = 0;
+
 	pr_info("%s - version %s\n",
 	       igb_driver_string, igb_driver_version);
 	pr_info("%s\n", igb_copyright);
@@ -2161,6 +2170,11 @@ 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,
+	.ndo_select_queue	= igb_tsn_select_queue,
+#endif	/* CONFIG_IGB_TSN */
 	.ndo_fix_features	= igb_fix_features,
 	.ndo_set_features	= igb_set_features,
 	.ndo_fdb_add		= igb_ndo_fdb_add,
@@ -2682,6 +2696,9 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	/* do hw tstamp init after resetting */
 	igb_ptp_init(adapter);
 
+	if (use_tsn)
+		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) {
@@ -3009,6 +3026,21 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter)
 
 	adapter->rss_queues = min_t(u32, max_rss_queues, num_online_cpus());
 
+#if IS_ENABLED(CONFIG_IGB_TSN)
+	/* For I210: If we are using TSN, we don't want to use num_online_cpus(),
+	 * as we need 4 Tx-queues.
+	 *
+	 * Rx is another matter, but in time we probably want to assign
+	 * Rx-0 to class A, Rx-1 to B, -2 to PTP/MSRP control etc and -3
+	 * to all other traffic.
+	 */
+	if (use_tsn && hw->mac.type == e1000_i210) {
+		adapter->rss_queues = max_rss_queues;
+		pr_info("igb_init_queue_configuration: rss_queues=%u\n",
+			adapter->rss_queues);
+	}
+#endif	/* IGB_TSN */
+
 	igb_set_flag_queue_pairs(adapter, max_rss_queues);
 }
 
@@ -3397,7 +3429,8 @@ void igb_configure_tx_ring(struct igb_adapter *adapter,
 	txdctl |= IGB_TX_PTHRESH;
 	txdctl |= IGB_TX_HTHRESH << 8;
 	txdctl |= IGB_TX_WTHRESH << 16;
-
+	if (ring->flags & IGB_FLAG_QAV_PRIO)
+		txdctl |= E1000_TXDCTL_PRIORITY;
 	txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
 	wr32(E1000_TXDCTL(reg_idx), txdctl);
 }
@@ -5345,8 +5378,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..dd5d9ed
--- /dev/null
+++ b/drivers/net/ethernet/intel/igb/igb_tsn.c
@@ -0,0 +1,468 @@
+/*
+ * 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_UNLIM_CREDIT      0xFFFFFFFF
+#define E1000_TQAVCH_ZERO_CREDIT       0x80000000
+#define E1000_LINK_RATE		       0x7735
+
+/* 0:15  idleSlope
+ * 16:29 reserved
+ * 30    reserved
+ * 31    queue mode, 0=strict, 1=SR mode
+ */
+#define E1000_TQAVCC_QUEUEMODE         0x80000000
+#define E1000_TQAVCC_IDLE_SLOPE_MASK   0x0000ffff
+
+/* 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 debug_tsn = -1;
+module_param(debug_tsn, int, 0);
+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 (netdev=%d, real=%d), num_rx_queues=%d (netdev=%d, real=%d)\n",
+		adapter->num_tx_queues, adapter->netdev->num_tx_queues, adapter->netdev->real_num_tx_queues,
+		adapter->num_rx_queues, adapter->netdev->num_rx_queues, adapter->netdev->real_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=0x%x\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=0x%02x, QueueMode=%s\n",
+		 val % 0xff,
+		 val > 31 ? "Stream reservation" : "Strict priority");
+	val = rd32(E1000_TQAVCC(1));
+	dev_info(dev, "E1000_TQAVCC1: idleSlope=0x%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 txpbsize;
+	u32 tqavctrl;
+
+	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);
+
+	if (adapter->num_tx_queues != 4) {
+		pr_err("IGB_TSN: ERROR, not enough TX-queues available, need 4, got %d\n",
+			adapter->num_tx_queues);
+		return;
+	}
+
+	/* setup the Transmit Descriptor Control (see 8.12.15)
+	 *
+	 * Set a flag for priority-queue and call igb_configure_tx_ring() in igb_main.c
+	 */
+	adapter->tx_ring[0]->flags |= IGB_FLAG_QAV_PRIO;
+	adapter->tx_ring[1]->flags |= IGB_FLAG_QAV_PRIO;
+	igb_configure_tx_ring(adapter, adapter->tx_ring[0]);
+	igb_configure_tx_ring(adapter, adapter->tx_ring[1]);
+
+	/* Set Tx packet buffer size assignment, see 7.2.7.7 in i210
+	 * PB0: 8kB (default 20 kB)
+	 * PB1: 8kB (default  0 pkB)
+	 * PB2: 4kB (default  0 kB)
+	 * PB3: 4kB (default  0 kB)
+	 *
+	 * os2bmcsize: 2kB (default 4 kB)
+
+	 * UPDATE: set os2bmcpbsize to 0, then we can drop setting RX
+	 * packet buffer
+	 *
+	 * sumTx: 24kB (26kB)
+	 *
+	 * Rxpbsize: 0x20 (32kB default 34kB
+	 * bmc2ossize: 0x02 (default 0x02)
+	 * sumRx: 34kB
+	 *
+	 * Total 60kB (see 4.5.9)
+	 *
+	 * See 8.3.1 && 8.3.2 for fields
+	 */
+	txpbsize = (0 << 30 | 0x00 << 24 | 0x04 << 18 | 0x04 << 12 | 0x08 << 6 | 0x08);
+	wr32(E1000_ITPBS, txpbsize);
+
+	/* Since we dropped os2bmcsize, we do not have to change the
+	 * default here after all */
+	/* 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);
+
+	/* For now, only set CreditBased shaper for A and B, do not set
+	 * idleSlope as we have not yet gotten any streams. Set HiCredit
+	 * to be unlimitied (this violates the 75% 'default' boundary
+	 *
+	 * 8.12.19
+	 */
+	wr32(E1000_TQAVCC(0), E1000_TQAVCC_QUEUEMODE);
+	wr32(E1000_TQAVCC(1), E1000_TQAVCC_QUEUEMODE);
+	wr32(E1000_TQAVHC(0), E1000_TQAVCH_UNLIM_CREDIT);
+	wr32(E1000_TQAVHC(1), E1000_TQAVCH_UNLIM_CREDIT);
+
+	/* 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) is off (we do not want
+	 *			     time-triggered launch)
+	 * - Wait for SR queues to ensure that launch time is always valid.
+	 * - Set ~10us wait-time-delta, 32ns granularity
+	 */
+	tqavctrl = E1000_TQAVCTRL_TXMODE      | \
+		E1000_TQAVCTRL_DATA_FETCH_ARB | \
+		E1000_TQAVCTRL_DATA_TRAN_ARB  | \
+		E1000_TQAVCTRL_SP_WAIT_SR     | \
+		320 << E1000_TQAVCTRL_FETCH_TM_SHIFT;
+	wr32(E1000_TQAVCTRL, tqavctrl);
+
+	/* 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;
+	adapter->tsn_vlan_added = 0;
+
+	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);
+	return adapter->tsn_ready == 1;
+}
+
+/* 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, u8 add_link, u8 pcp_hi, u8 pcp_lo)
+{
+	s32 idle_slope = 0;
+	s32 frames_pr_ms;
+	s32 new_is = 0;
+	u32 tqavcc;
+	int err;
+
+	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;
+	}
+
+	/*
+	 * FIXME: This should disable EEE and (possibly) enable it when
+	 * removing the last link.
+	 *
+	 * FIXME: This is only done the first time and have the
+	 * potentional of never being reset as we do not count the
+	 * number of configured links.
+	 *
+	 * This means that if all links goes away and the network
+	 * reconfigures the domain to use different PCPs, we will drop
+	 * that.
+	 *
+	 * FIXME: this update is racy
+	 */
+	if (add_link && !adapter->tsn_vlan_added) {
+		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();
+		adapter->pcp_hi = pcp_hi & 0x7;
+		adapter->pcp_lo = pcp_lo & 0x7;
+		adapter->tsn_vlan_added = 1;
+	}
+
+	/* we currently drop hicred (we have set this to unlim to ease calculation) */
+	/* Grab current values of idle_slope
+	 */
+	switch(class) {
+	case SR_CLASS_A:
+		tqavcc = rd32(E1000_TQAVCC(0));
+		frames_pr_ms = 8;
+		break;
+	case SR_CLASS_B:
+		tqavcc = rd32(E1000_TQAVCC(1));
+		frames_pr_ms = 4;
+		break;
+	default:
+		pr_err("igb_tsn: unknown traffic-class, aborting configuration\n");
+		return -EINVAL;
+	}
+	idle_slope = tqavcc & E1000_TQAVCC_IDLE_SLOPE_MASK;
+
+	/* Calculate new idle slope and add to appropriate idle_slope
+	 * idle_slope = BW * linkrate * 2 (0r 0.2 for 100Mbit)
+	 * BW: % of total bandwidth
+	 *
+	 * E1000_LINK_RATE: 0x7735
+	 * LINE_SPEED: 1e9
+	 */
+	new_is = (s32)framesize * frames_pr_ms * E1000_LINK_RATE * 2 * 8;
+	if (new_is % 1000000)
+		new_is += 1000000;
+	new_is /= 1000000;
+
+	pr_info("Framesize=%u,E1000_LINK_RATE=%u,class=%s,new_is=%s%d,current_is=%u\n",
+		framesize,E1000_LINK_RATE,
+		(class == SR_CLASS_A ? "A" : "B"),
+		(add_link ? "+" : "-"),
+		new_is, idle_slope);
+	if (!add_link)
+		new_is *= -1;
+	idle_slope += new_is;
+
+	tqavcc &= ~E1000_TQAVCC_IDLE_SLOPE_MASK;
+	tqavcc |= idle_slope & E1000_TQAVCC_IDLE_SLOPE_MASK;
+	if (class == SR_CLASS_A) {
+		wr32(E1000_TQAVCC(0), tqavcc);
+		adapter->sra_idleslope_res += (s16)new_is;
+	} else {
+		wr32(E1000_TQAVCC(1), tqavcc);
+		adapter->srb_idleslope_res += (s16)new_is;
+	}
+
+	if (adapter->sra_idleslope_res == 0 && adapter->srb_idleslope_res == 0) {
+		/* removing last stream going through NIC, drop vlan and
+		 * make it possible to change PCP */
+		rtnl_lock();
+		vlan_vid_del(netdev, htons(ETH_P_8021Q), vid);
+		adapter->tsn_vlan_added = 0;
+		rtnl_unlock();
+	}
+	_tsn_dump_regs(netdev_priv(netdev));
+	pr_info("igb_tsn_link_configure: done setting up TSN, idle_slope: %u,for frame: %u\n",
+		idle_slope, new_is);
+
+	return 0;
+}
+
+u16 igb_tsn_select_queue(struct net_device *netdev, struct sk_buff *skb,
+			void *accel_priv, select_queue_fallback_t fallback)
+{
+	struct igb_adapter *adapter = netdev_priv(netdev);
+	if (!adapter)
+		return fallback(netdev, skb);
+
+	/* we only return the special queue(s) for tsn-traffic, and gPTP
+	 * otherwise we pick the last queue
+	 */
+	if (igb_tsn_capable(netdev)) {
+		switch (vlan_get_protocol(skb)) {
+		case htons(ETH_P_TSN):
+			if (skb->priority == adapter->pcp_hi)
+				return 0;
+			if (skb->priority == adapter->pcp_lo)
+				return 1;
+			pr_err("igb_tsn select queu: unknown PCP:%u, expected either hi=%u or lo=%u\n",
+				skb->priority, adapter->pcp_hi, adapter->pcp_lo);
+			break;
+		case htons(ETH_P_1588):
+			/* PTP should be sent via tx-queue 2 */
+			return 2;
+		default:
+			/* rest via 3 */
+			return  3;
+		}
+	}
+	return fallback(netdev, skb);
+}
+#endif	/* #if IS_ENABLED(CONFIG_IGB_TSN) */
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1543645 — [TSN RFC v2 1/9] igb: add missing fields to TXDCTL-register

Fromhenrik@austad.us
Date2016-12-16 19:10 +0100
Subject[TSN RFC v2 1/9] igb: add missing fields to TXDCTL-register
Message-ID<sP57c-75S-31@gated-at.bofh.it>
In reply to#1543643
From: Henrik Austad <henrik@austad.us>

The current list of E1000_TXDCTL-registers is incomplete. This adds the
missing parts for the Transmit Descriptor Control (TXDCTL) register.

The rest of these values (threshold for descriptor read/write) for
TXDCTL seems to be defined in igb/igb.h, not sure why this is split
though.

It seems that this was left out in the commit that added support for
82575 Gigabit Ethernet driver 9d5c8243 (igb: PCI-Express 82575 Gigabit
Ethernet driver).

Cc: linux-kernel@vger.kernel.org
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Cc: intel-wired-lan@lists.osuosl.org
Signed-off-by: Henrik Austad <haustad@cisco.com>
---
 drivers/net/ethernet/intel/igb/e1000_82575.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h
index acf0605..7faa482 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.h
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.h
@@ -158,7 +158,11 @@ struct e1000_adv_tx_context_desc {
 
 /* Additional Transmit Descriptor Control definitions */
 #define E1000_TXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Tx Queue */
+
+/* Transmit Software Flush, sw-triggered desc writeback */
+#define E1000_TXDCTL_SWFLSH        0x04000000
 /* Tx Queue Arbitration Priority 0=low, 1=high */
+#define E1000_TXDCTL_PRIORITY      0x08000000
 
 /* Additional Receive Descriptor Control definitions */
 #define E1000_RXDCTL_QUEUE_ENABLE  0x02000000 /* Enable specific Rx Queue */
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1543646 — [TSN RFC v2 8/9] AVB ALSA - Add ALSA shim for TSN

Fromhenrik@austad.us
Date2016-12-16 19:10 +0100
Subject[TSN RFC v2 8/9] AVB ALSA - Add ALSA shim for TSN
Message-ID<sP57c-75S-41@gated-at.bofh.it>
In reply to#1543643
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 > shim
echo on > enabled

     aplay -Ddefault:CARD=avb music.wav

  or

     arecord -r48000 -c2 -f S16_LE | aplay -Ddefault:CARD=avb

alternatively, if the device is set as Listener;

     arecord -r48000 -c2 -f S16_LE -Ddefault:CARD=avb > file.wav

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           |   2 +-
 drivers/media/avb/Makefile       |   5 +
 drivers/media/avb/avb_alsa.c     | 793 +++++++++++++++++++++++++++++++++++++++
 drivers/media/avb/tsn_iec61883.h | 152 ++++++++
 5 files changed, 966 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 7b85402..8250aff 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -221,3 +221,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 0deaa93..9dfee62 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -34,4 +34,4 @@ obj-y += rc/
 
 obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ spi/
 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..bd202f5
--- /dev/null
+++ b/drivers/media/avb/avb_alsa.c
@@ -0,0 +1,793 @@
+/* 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 <sound/initval.h>
+#include <linux/tsn.h>
+#include "tsn_iec61883.h"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for AVB soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for AVB soundcard.");
+
+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
+ *
+ * report absolute hardware link audio time, not reset on startup
+ * (SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME). We are using gPTP time for
+ * arrival/transmit of frames, this will be handled by
+ * tsn_(read|write)_buffer_net()
+ */
+static struct snd_pcm_hardware snd_avb_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME,
+	.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,
+	/* we currently expect either class A or B, where A has the
+	 * smallest frames. For 44.1, you can get 11 sample-sets in one
+	 * frame, and for 2ch S16_LE, this gives us 44 bytes.
+	 */
+	.period_bytes_min = 44,
+	.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;
+}
+
+static int snd_avb_get_time_info(struct snd_pcm_substream *substream,
+				struct timespec *system_ts, struct timespec *audio_ts,
+				struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+				struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+	struct avb_chip *avb_chip = snd_pcm_substream_chip(substream);
+	struct tsn_link *link = avb_chip->link;
+	u64 nsec;
+
+	snd_pcm_gettime(substream->runtime, system_ts);
+
+	/* We only support link-time from sample, absolute (no reset) */
+	if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME) &&
+		(audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE)) {
+		tsn_lock(link);
+		nsec = link->ts_net_ns;
+		tsn_unlock(link);
+
+		*audio_ts = ns_to_timespec(nsec);
+		audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE;
+
+		/* this should be accurate, but leave it as inaccurate
+		 * until we know the level for gPTP-clock in the system
+		 * accuracy_report = 1 (timestamp is accurate to within accuracy)
+		 * accuracy: in ns
+		 */
+		audio_tstamp_report->accuracy_report = 0;
+		audio_tstamp_report->accuracy = 42;
+	} else {
+		audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+	}
+
+	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,
+	.get_time_info = snd_avb_get_time_info,
+};
+
+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,
+	.get_time_info = snd_avb_get_time_info,
+};
+
+/*
+ * 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 == SR_CLASS_B) {
+			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 == SR_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;
+	int dev = devptr->id;
+
+	pr_info("%s: starting\n", __func__);
+
+	err = snd_card_new(&devptr->dev, index[dev], id[dev], 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..167bfd8
--- /dev/null
+++ b/drivers/media/avb/tsn_iec61883.h
@@ -0,0 +1,152 @@
+#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 {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	u8 tag:2;
+	u8 channel:6;
+	u8 tcode:4;
+	u8 sy:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u8 channel:6;
+	u8 tag:2;
+	u8 sy:4;
+	u8 sy:4;
+#else
+#error "Unknown Endianness, cannot determine bitfield ordering"
+#endif
+} __packed;
+
+struct iec61883_audio_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	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;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u8 cip_1:2;
+	u8 sid:6;
+
+	u8 dbs:8;
+
+	u8 fn:2;
+	u8 qpc:3;
+	u8 sph:1;
+	u8 rsv:2;		/* reserved */
+
+	u8 dbc;
+
+	u8 cip_2:2;
+	u8 fmt:6;
+#else
+#error "Unknown Endianness, cannot determine bitfield ordering"
+#endif
+	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 != TSN_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 = TSN_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]


#1543647 — [TSN RFC v2 5/9] Add TSN header for the driver

Fromhenrik@austad.us
Date2016-12-16 19:10 +0100
Subject[TSN RFC v2 5/9] Add TSN header for the driver
Message-ID<sP57c-75S-47@gated-at.bofh.it>
In reply to#1543643
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 | 952 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 952 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..9123b25
--- /dev/null
+++ b/include/linux/tsn.h
@@ -0,0 +1,952 @@
+/*   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/kthread.h>
+#include <linux/configfs.h>
+#include <linux/hrtimer.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+/*
+ * List of current subtype fields in the common header of AVTPDU
+ *
+ * Note: AVTPDU is a remnant of the standards from when it was AVB.
+ *
+ * The list has been updated with the recent values from IEEE 1722, draft 16.
+ */
+enum avtp_subtype {
+	TSN_61883_IIDC = 0,	/* IEC 61883/IIDC Format */
+	TSN_MMA_STREAM,		/* MMA Streams */
+	TSN_AAF,		/* AVTP Audio Format */
+	TSN_CVF,		/* Compressed Video Format */
+	TSN_CRF,		/* Clock Reference Format */
+	TSN_TSCF,		/* Time-Synchronous Control Format */
+	TSN_SVF,		/* SDI Video Format */
+	TSN_RVF,		/* Raw Video Format */
+	/* 0x08 - 0x6D reserved */
+	TSN_AEF_CONTINOUS = 0x6e, /* AES Encrypted Format Continous */
+	TSN_VSF_STREAM,		/* Vendor Specific Format Stream */
+	/* 0x70 - 0x7e reserved */
+	TSN_EF_STREAM = 0x7f,	/* Experimental Format Stream */
+	/* 0x80 - 0x81 reserved */
+	TSN_NTSCF = 0x82,	/* Non Time-Synchronous Control Format */
+	/* 0x83 - 0xed reserved */
+	TSN_ESCF = 0xec,	/* ECC Signed Control Format */
+	TSN_EECF,		/* ECC Encrypted Control Format */
+	TSN_AEF_DISCRETE,	/* AES Encrypted Format Discrete */
+	/* 0xef - 0xf9 reserved */
+	TSN_ADP = 0xfa,		/* AVDECC Discovery Protocol */
+	TSN_AECP,		/* AVDECC Enumeration and Control Protocol */
+	TSN_ACMP,		/* AVDECC Connection Management Protocol */
+	/* 0xfd reserved */
+	TSN_MAAP = 0xfe,	/* MAAP Protocol */
+	TSN_EF_CONTROL,		/* Experimental Format Control */
+};
+
+/* Link-states to help error-recovery detected from irq context.
+ */
+enum link_states {
+	LINK_OFF = 0,
+	LINK_RUNNING,
+	LINK_ERROR,
+};
+
+
+/*		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;
+	spinlock_t 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;
+
+	struct task_struct *tsn_thread;
+	int should_run;
+
+	size_t num_avail;
+};
+
+static inline void tsn_list_lock(struct tsn_list *list)
+{
+	spin_lock(&list->lock);
+}
+static inline void tsn_list_unlock(struct tsn_list *list)
+{
+	spin_unlock(&list->lock);
+}
+
+/**
+ * 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;
+
+	/*
+	 * Any AVTP data stream must set the 802.1Q vlan id and priority
+	 * Code point. This should be obtained from MSRP, default values
+	 * are:
+	 *
+	 * 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;
+};
+
+struct tsn_shim_ops;
+/**
+ * tsn_link - Structure describing a single TSN link
+ *
+ */
+struct tsn_link {
+	/* Locks for protecting the link
+	 *
+	 * Due to how we do Rx and Tx, we need different types of locks
+	 * in these settings. A link _cannot_ be both, so even though
+	 * this way of doing it is ugly, it should be safe.
+	 *
+	 * Reader: must disable interrupt as we take the lock in rx-handler (Network bh)
+	 * Talker: must not disable interrupt
+	 */
+	spinlock_t tlock;
+	raw_spinlock_t llock;
+	unsigned long lflags;
+
+	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 what attributes
+	 * (most actually) that can be changed.
+	 *
+	 */
+	atomic_t link_state;
+
+	/* keep track of how many frames we have sent (for debugging) */
+	u64 frames_sent;
+
+	/* timestamp for last frame going in/out over the network, delta
+	 * and avg delta
+	 */
+	u64 ts_net_ns;
+	u64 ts_delta_ns;
+
+	/* simple, exponential smoothing of the time between received
+	 * samples. This is useful for shims that need to calculate an
+	 * offset into the buffer of received data.
+	 * exp_avg = alpha * ts_delta_ns + (1-alpha)exp_avg_{-1}
+	 * avg_delta_ns = ts_delta_ns * alpha_scale + avg_delta_ns * (1 - alpha_scale)
+	 */
+	u64 ts_exp_avg;
+
+	/* alpha_scale is currently in the range 0 - (2^14 - 1) because
+	 * 16384 different values is "probably enough" for a smoothed
+	 * avg.
+	 */
+	u16 ts_exp_alpha;
+
+	/* 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 to keep skb when we overproduce so that we can do a
+	 * somewhat sane backoff.
+	 */
+	struct sk_buff *old_skb;
+
+	/*
+	 * 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 of different classes, currently A or B
+	 *
+	 * ClassA: every 125us
+	 * ClassB: every 250us
+	 *
+	 * This will also affect how large each frame will be and will
+	 * also grab the PCP from the NIC-struct
+	 */
+	enum sr_class class;
+
+	u16 vlan_id;
+
+	u8 remote_mac[6];
+};
+
+static inline void tsn_lock(struct tsn_link *link)
+{
+	if (link->estype_talker)
+		spin_lock(&link->tlock);
+	else
+		raw_spin_lock_irqsave(&link->llock, link->lflags);
+}
+
+static inline void tsn_unlock(struct tsn_link *link)
+{
+	if (link->estype_talker)
+		spin_unlock(&link->tlock);
+	else
+		raw_spin_unlock_irqrestore(&link->llock, link->lflags);
+}
+
+void tsn_lock_init(struct tsn_link *link);
+
+/**
+ * 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->link_state, LINK_RUNNING);
+}
+
+/**
+ * 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->link_state, LINK_OFF);
+}
+
+static inline int tsn_link_is_off(struct tsn_link *link)
+{
+	if (link)
+		return atomic_read(&link->link_state) == LINK_OFF;
+	return 0;
+}
+/**
+ * tsn_link_err - mark link as having an error.
+ *
+ * Link will be ignored by timer callback after being marked as in error
+ * and will be torn down upon next non-irq handling of link. After being
+ * torn down, it will be marked as 'off'.
+ *
+ *@link: link in error
+ */
+static inline void tsn_link_err(struct tsn_link *link)
+{
+	if (link)
+		atomic_set(&link->link_state, LINK_ERROR);
+}
+static inline int tsn_link_is_err(struct tsn_link *link)
+{
+	if (link)
+		return atomic_read(&link->link_state) == LINK_ERROR;
+	return 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->link_state) == LINK_RUNNING;
+	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;
+}
+
+/**
+ * tsn_update_net_time - update timestamp for latest activity on the net-side
+ *
+ * This function expects link to be locked, i.e. no-one else is updating
+ * the fields. Typically called from tsn_rx_handler (which grabs tsn_link()).
+ *
+ * @param link active link
+ * @param tim_ns timestamp
+ * @param increment the number if times buffer has been updated
+ *
+ * @returns 0 on success, negative on error.
+ */
+int tsn_update_net_time(struct tsn_link *link, u64 time_ns, int increment);
+
+/**
+ * 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;
+}
+
+static inline u8 sr_class_to_pcp(struct tsn_nic *nic, enum sr_class class)
+{
+	if (!nic)
+		return 0;
+	switch (class) {
+	case SR_CLASS_A:
+		return nic->pcp_a;
+	case SR_CLASS_B:
+		return nic->pcp_b;
+		/* room for future class C & D */
+	default:
+		pr_err("Unknown class in mapping %d\n", class);
+	}
+	return 0;
+}
+
+#endif	/* _TSN_H */
-- 
2.7.4

[toc] | [prev] | [next] | [standalone]


#1543795 — Re: [TSN RFC v2 5/9] Add TSN header for the driver

FromRichard Cochran <richardcochran@gmail.com>
Date2016-12-16 23:20 +0100
SubjectRe: [TSN RFC v2 5/9] Add TSN header for the driver
Message-ID<sP918-11E-25@gated-at.bofh.it>
In reply to#1543647
On Fri, Dec 16, 2016 at 06:59:09PM +0100, henrik@austad.us wrote:
> +/*
> + * List of current subtype fields in the common header of AVTPDU
> + *
> + * Note: AVTPDU is a remnant of the standards from when it was AVB.
> + *
> + * The list has been updated with the recent values from IEEE 1722, draft 16.
> + */
> +enum avtp_subtype {
> +	TSN_61883_IIDC = 0,	/* IEC 61883/IIDC Format */
> +	TSN_MMA_STREAM,		/* MMA Streams */
> +	TSN_AAF,		/* AVTP Audio Format */
> +	TSN_CVF,		/* Compressed Video Format */
> +	TSN_CRF,		/* Clock Reference Format */
> +	TSN_TSCF,		/* Time-Synchronous Control Format */
> +	TSN_SVF,		/* SDI Video Format */
> +	TSN_RVF,		/* Raw Video Format */
> +	/* 0x08 - 0x6D reserved */
> +	TSN_AEF_CONTINOUS = 0x6e, /* AES Encrypted Format Continous */
> +	TSN_VSF_STREAM,		/* Vendor Specific Format Stream */
> +	/* 0x70 - 0x7e reserved */
> +	TSN_EF_STREAM = 0x7f,	/* Experimental Format Stream */
> +	/* 0x80 - 0x81 reserved */
> +	TSN_NTSCF = 0x82,	/* Non Time-Synchronous Control Format */
> +	/* 0x83 - 0xed reserved */
> +	TSN_ESCF = 0xec,	/* ECC Signed Control Format */
> +	TSN_EECF,		/* ECC Encrypted Control Format */
> +	TSN_AEF_DISCRETE,	/* AES Encrypted Format Discrete */
> +	/* 0xef - 0xf9 reserved */
> +	TSN_ADP = 0xfa,		/* AVDECC Discovery Protocol */
> +	TSN_AECP,		/* AVDECC Enumeration and Control Protocol */
> +	TSN_ACMP,		/* AVDECC Connection Management Protocol */
> +	/* 0xfd reserved */
> +	TSN_MAAP = 0xfe,	/* MAAP Protocol */
> +	TSN_EF_CONTROL,		/* Experimental Format Control */
> +};

The kernel shouldn't be in the business of assembling media packets.

Thanks,
Richard

[toc] | [prev] | [next] | [standalone]


#1543943 — Re: [TSN RFC v2 5/9] Add TSN header for the driver

FromHenrik Austad <henrik@austad.us>
Date2016-12-17 10:00 +0100
SubjectRe: [TSN RFC v2 5/9] Add TSN header for the driver
Message-ID<sPj0t-7mO-9@gated-at.bofh.it>
In reply to#1543795

[Multipart message — attachments visible in raw view] — view raw

On Fri, Dec 16, 2016 at 11:09:38PM +0100, Richard Cochran wrote:
> On Fri, Dec 16, 2016 at 06:59:09PM +0100, henrik@austad.us wrote:
> > +/*
> > + * List of current subtype fields in the common header of AVTPDU
> > + *
> > + * Note: AVTPDU is a remnant of the standards from when it was AVB.
> > + *
> > + * The list has been updated with the recent values from IEEE 1722, draft 16.
> > + */
> > +enum avtp_subtype {
> > +	TSN_61883_IIDC = 0,	/* IEC 61883/IIDC Format */
> > +	TSN_MMA_STREAM,		/* MMA Streams */
> > +	TSN_AAF,		/* AVTP Audio Format */
> > +	TSN_CVF,		/* Compressed Video Format */
> > +	TSN_CRF,		/* Clock Reference Format */
> > +	TSN_TSCF,		/* Time-Synchronous Control Format */
> > +	TSN_SVF,		/* SDI Video Format */
> > +	TSN_RVF,		/* Raw Video Format */
> > +	/* 0x08 - 0x6D reserved */
> > +	TSN_AEF_CONTINOUS = 0x6e, /* AES Encrypted Format Continous */
> > +	TSN_VSF_STREAM,		/* Vendor Specific Format Stream */
> > +	/* 0x70 - 0x7e reserved */
> > +	TSN_EF_STREAM = 0x7f,	/* Experimental Format Stream */
> > +	/* 0x80 - 0x81 reserved */
> > +	TSN_NTSCF = 0x82,	/* Non Time-Synchronous Control Format */
> > +	/* 0x83 - 0xed reserved */
> > +	TSN_ESCF = 0xec,	/* ECC Signed Control Format */
> > +	TSN_EECF,		/* ECC Encrypted Control Format */
> > +	TSN_AEF_DISCRETE,	/* AES Encrypted Format Discrete */
> > +	/* 0xef - 0xf9 reserved */
> > +	TSN_ADP = 0xfa,		/* AVDECC Discovery Protocol */
> > +	TSN_AECP,		/* AVDECC Enumeration and Control Protocol */
> > +	TSN_ACMP,		/* AVDECC Connection Management Protocol */
> > +	/* 0xfd reserved */
> > +	TSN_MAAP = 0xfe,	/* MAAP Protocol */
> > +	TSN_EF_CONTROL,		/* Experimental Format Control */
> > +};
> 
> The kernel shouldn't be in the business of assembling media packets.

No, but assembling the packets and shipping frames to a destination is not 
neccessarily the same thing.

A nice workflow would be to signal to the shim that "I'm sending a 
compressed video format" and then the shim/tsn_core will ship out the 
frames over the network - and then you need to set TSN_CVF as subtype in 
each header.

That does not that mean you should do H.264 encode/decode *in* the kernel

Perhaps this is better placed in include/uapi/tsn.h so that userspace and 
kernel share the same header?

-- 
Henrik Austad

[toc] | [prev] | [next] | [standalone]


#1543653

FromGreg <gvrose8192@gmail.com>
Date2016-12-16 19:20 +0100
Message-ID<sP5gS-799-7@gated-at.bofh.it>
In reply to#1543643
On Fri, 2016-12-16 at 18:59 +0100, henrik@austad.us wrote:
> From: Henrik Austad <haustad@cisco.com>
> 
> 
> 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. This also includes running an
> appropriate PTP daemon (TSN favors gPTP).

I suggest using a generic netlink interface to communicate with the
driver to set up and/or configure your drivers.

I think configfs is frowned upon for network drivers.  YMMV.

- Greg

> 
> 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 dev_queue_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 an AVB-capable NIC which is
> available as a PCI-e card).
> 
> What remains (tl;dr: a lot) a.k.a "Known problems" or "working on it!"
> - tie to (g)PTP properly, currently using ktime_get() for presentation
>   time
> - get time from shim into TSN
> - let shim create/manage buffer
> - redo parts of the link-stuff using RCUs, the current setup is a bit
>   clumsy.
> - The igb-driver does not work properly when compiled with IGB_TSN, some
>   details in setting the register values needs to be figured out. I am
>   working on this, but as it stands, the best bet is to load tsn using
>   in_debug=1 to bypass the capability-check. I have had e1000 and sky2
>   running for several days without crashing, igb crashes and burns
>   violently.
> - The ALSA driver does not handle multiple devices very well and is a
>   work in progress.
> 
> * v2: changes since v1
> 
> Changes since v1
> - updated to latest upstream kernel (v4.8)
> - set dedicated enabled-attribute and let shim be stored in own (support
>   future plan for enabling per-shim attributes)
> - fixed endianess issue in bitfields used in tsn-structs
> - Updated some of the trace-events to use trace_class
> - Fix various silly typos
> - Handle disabling of link from hrtimer a bit more gracefully (that
>   actually works-ish).
> - use old skb and size of skb when that is set (Reporte by Nikita)
> - Move PCP-codes to NIC and not in the link itself
> - Allow TSN-capable card to be loaded even when in debug-mode (and do
>   not enforce TSN behaviour)
> - Start hooking into ALSA's get_time_info hooks (very much incomplete)
> - use threads for sending frames, wake from hrtimer-callback.
>   This also queues up awaiting timers if we fail to complete the
>   transmit before another timer arrives, it will immediately execute
>   another iteration, so no events should be lost. That being said,
>   should this happen, it is a clear bug as we really should complete
>   well before the next interval.
> - Cleanup link-locking and differentiate between Talker and Listener (as
>   Listener grab link-lock from IRQ context)
> - Change list-lock to spinlock as we may need to take a link-lock whilst
>   holding the master list-lock.
> - Do a more careful dance holding the spinlocks to regions only doing
>   actual update.
> 
> Network driver (I210 only)
> - bring up all Tx-/Rx-queues when igb is in TSN-mode regardless of how
>   many CPUs the system has for I210
> - Correctly calculate the idle_slope in I210's configure hook
> - Update igb-driver with queue-select and return correct queue when
>   sending TSN-frames
> - add IGB_FLAG_QAV_PRIO flag to igb_adapter (to handle proper config of
>   tx-ring when adapter is brought up.
> - add TXDCTL logic (part of preparatory work for TSN) to igb-driver
> - Improve SR(A|B) accountingo
> 
> ALSA Shim
> - Allow userspace to grab much smaller chunks of data (down to a single
>   Class A frame for S16_LE 2ch 48kHz).
> - Create the card with index/id pattern to avoid collision with other
>   cards.
> * v1
> 
> Before reading on - this is not even beta, but I'd really appreciate if
> people would comment on the overall architecture and perhaps provide
> some pointers to where I should improve/fix/update
> - thanks!
> 
> 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.
> 
> There are at least one AVB-driver (the AV-part of TSN) in the kernel
> already. 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.
> 
> Henrik Austad (9):
>   igb: add missing fields to TXDCTL-register
>   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                    |  345 ++++++++
>  MAINTAINERS                                  |   14 +
>  drivers/media/Kconfig                        |   15 +
>  drivers/media/Makefile                       |    2 +-
>  drivers/media/avb/Makefile                   |    5 +
>  drivers/media/avb/avb_alsa.c                 |  793 +++++++++++++++++
>  drivers/media/avb/tsn_iec61883.h             |  152 ++++
>  drivers/net/ethernet/intel/Kconfig           |   18 +
>  drivers/net/ethernet/intel/igb/Makefile      |    2 +-
>  drivers/net/ethernet/intel/igb/e1000_82575.h |    4 +
>  drivers/net/ethernet/intel/igb/igb.h         |   26 +
>  drivers/net/ethernet/intel/igb/igb_main.c    |   39 +-
>  drivers/net/ethernet/intel/igb/igb_tsn.c     |  468 ++++++++++
>  include/linux/netdevice.h                    |   44 +
>  include/linux/tsn.h                          |  952 +++++++++++++++++++++
>  include/trace/events/tsn.h                   |  333 ++++++++
>  net/Kconfig                                  |    1 +
>  net/Makefile                                 |    1 +
>  net/tsn/Kconfig                              |   32 +
>  net/tsn/Makefile                             |    6 +
>  net/tsn/tsn_configfs.c                       |  673 +++++++++++++++
>  net/tsn/tsn_core.c                           | 1189 ++++++++++++++++++++++++++
>  net/tsn/tsn_header.c                         |  162 ++++
>  net/tsn/tsn_internal.h                       |  397 +++++++++
>  net/tsn/tsn_net.c                            |  392 +++++++++
>  25 files changed, 6061 insertions(+), 4 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] | [prev] | [next] | [standalone]


#1543657

FromDavid Miller <davem@davemloft.net>
Date2016-12-16 19:30 +0100
Message-ID<sP5qy-7cn-15@gated-at.bofh.it>
In reply to#1543653
From: Greg <gvrose8192@gmail.com>
Date: Fri, 16 Dec 2016 10:12:44 -0800

> On Fri, 2016-12-16 at 18:59 +0100, henrik@austad.us wrote:
>> From: Henrik Austad <haustad@cisco.com>
>> 
>> 
>> 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. This also includes running an
>> appropriate PTP daemon (TSN favors gPTP).
> 
> I suggest using a generic netlink interface to communicate with the
> driver to set up and/or configure your drivers.
> 
> I think configfs is frowned upon for network drivers.  YMMV.

Agreed.

[toc] | [prev] | [next] | [standalone]


#1543697

FromHenrik Austad <henrik@austad.us>
Date2016-12-16 20:10 +0100
Message-ID<sP63f-7FI-15@gated-at.bofh.it>
In reply to#1543657

[Multipart message — attachments visible in raw view] — view raw

On Fri, Dec 16, 2016 at 01:20:57PM -0500, David Miller wrote:
> From: Greg <gvrose8192@gmail.com>
> Date: Fri, 16 Dec 2016 10:12:44 -0800
> 
> > On Fri, 2016-12-16 at 18:59 +0100, henrik@austad.us wrote:
> >> From: Henrik Austad <haustad@cisco.com>
> >> 
> >> 
> >> 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. This also includes running an
> >> appropriate PTP daemon (TSN favors gPTP).
> > 
> > I suggest using a generic netlink interface to communicate with the
> > driver to set up and/or configure your drivers.
> > 
> > I think configfs is frowned upon for network drivers.  YMMV.
> 
> Agreed.

Ok - thanks!

I will have look at netlink and see if I can wrap my head around it and if 
I can apply it to how to bring the media-devices up once the TSN-link has 
been configured.

Thanks! :)

-- 
Henrik Austad

[toc] | [prev] | [next] | [standalone]


#1543779

FromRichard Cochran <richardcochran@gmail.com>
Date2016-12-16 23:10 +0100
Message-ID<sP8Rs-Yj-19@gated-at.bofh.it>
In reply to#1543643
On Fri, Dec 16, 2016 at 06:59:04PM +0100, henrik@austad.us wrote:
> 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.

I complained about configfs before, but you didn't listen.

> 2 new fields in netdev_ops have been introduced, and the Intel
> igb-driver has been updated (as this an AVB-capable NIC which is
> available as a PCI-e card).

The igb hacks show that you are on the wrong track.  We can and should
be able to support TSN without resorting to driver specific hacks and
module parameters.

> Before reading on - this is not even beta, but I'd really appreciate if
> people would comment on the overall architecture and perhaps provide
> some pointers to where I should improve/fix/update

As I said before about V1, this architecture stinks.  You appear to
have continued hacking along and posted the same design again.  Did
you even address any of the points I raised back then?

You are trying to put tons of code into the kernel that really belongs
in user space, and at the same time, you omit critical functions that
only the kernel can provide.

> There are at least one AVB-driver (the AV-part of TSN) in the kernel
> already.

And which driver is that?

Thanks,
Richard

[toc] | [prev] | [next] | [standalone]


#1543944

FromHenrik Austad <henrik@austad.us>
Date2016-12-17 10:10 +0100
Message-ID<sPjaa-7FU-17@gated-at.bofh.it>
In reply to#1543779

[Multipart message — attachments visible in raw view] — view raw

Hi Richard,

On Fri, Dec 16, 2016 at 11:05:30PM +0100, Richard Cochran wrote:
> On Fri, Dec 16, 2016 at 06:59:04PM +0100, henrik@austad.us wrote:
> > 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.
> 
> I complained about configfs before, but you didn't listen.

Yes you did, I remember quite well, and no, I didn't listen :)

At the time, there were other issues that I had to address. The 
configfs-part is fairly isolated. As I tried to explain the last round, 
the *reason* I've used ConfigFS thus far, is because it makes it pretty 
easy from userspace to signal the driver to create a new alsa-device.

And the reason I haven't changed configfs, is because so far, that part has 
worked fairly well and have made testing quite easy. At this stage, *this* 
is what is helpful, not a perfect interface. This does not mean that 
configfs is set in stone.

To clearify:
I'm sending out a new set now because, what I have works _fairly_ well for 
testing and a way to see what you can do with AVB. Using spotify to play 
music on random machines is quite entertaining.

It is by no means -done-, nor do I consider it done. I have been tight on 
time, and instead of sitting in an office polishing on some code, I thought 
it better to send out a new (and not done) set of patches so that others 
could see it still being worked on. If this turned out to be noise-only, I 
appologize!

> > 2 new fields in netdev_ops have been introduced, and the Intel
> > igb-driver has been updated (as this an AVB-capable NIC which is
> > available as a PCI-e card).
> 
> The igb hacks show that you are on the wrong track.  We can and should
> be able to support TSN without resorting to driver specific hacks and
> module parameters.

I was not able to find a sane way to change the mode of the NIC, some of 
the settings required to enable Qav-mode must be done when bringing the 
NIC up, so I needed hooks in _probe().

ANother elemnt needed is a way for tsn_core to ascertain if a NIC is 
capable of TSN or not (this would be ndo_tsn_capable)

Then finally, you need to update values in a per-tx-queue manner when a new 
stream is ready (hence ndo_tsn_link_configure).

What you mean by 'driver specific hacks' is not obvious though, TSN 
requires a set of fairly standardized parameters (priority code points, 
size of frames to send in a new stream and so on), adding this to the 
hw-registers in the NIC is an operation that will be common for all 
TSN-capable NICs.

> > Before reading on - this is not even beta, but I'd really appreciate if
> > people would comment on the overall architecture and perhaps provide
> > some pointers to where I should improve/fix/update
> 
> As I said before about V1, this architecture stinks. 

I like feedback when it's short, sweet and to the point
2 out of 3 ain't that bad ;)

> You appear to have continued hacking along and posted the same design 
> again.  Did you even address any of the points I raised back then?

So you did raise a lot of good points the last round, and no, I have not 
had the time to address them properly. That does not mean I do not *want* 
to (apart from configfs actually having worked quite nicely thus far and 
'shim' being a name I like ;)

From the last round of discussion:

> 1. A proper userland stack for AVDECC, MAAP, FQTSS, and so on.  The
>    OpenAVB project does not offer much beyond simple examples.

Yes, I fully agree, as far as I know, no-one is working on this. That being 
said, I have not paid much attention the userspace tooling lately. But all 
of this must be handled in userspace, having avdecc in the kernel would be 
an utter nightmare :)

> 2. A user space audio application that puts it all together, making
>   use of the services in #1, the linuxptp gPTP service, the ALSA
>   services, and the network connections.  This program will have all
>   the knowledge about packet formats, AV encodings, and the local HW
>   capabilities.  This program cannot yet be written, as we still need
>   some kernel work in the audio and networking subsystems.

And therein lies the problem. It cannot yet be written, so we have to start 
in *some* end. And as I repeatedly stated in June, I'm at an RFC here, 
trying to spark some interest and lure other developers in :)

Also, I really do not want a media-application to care about _where_ the 
frames are going. Sure, I see the issue of configuring a link, but that can 
be done from _outside_ the media-application. VLC (or aplay, or totem, or 
.. take your pick) should not have to worry about this.

Applications that require finer control over timestamping is easier to 
adapt to AVB than all the others, I'd rather add special knobs for those 
who are interested than adding a set of knobs that -all- applications must 
be aware of.

Could be that we are talking about the same thing, just from different 
perspectives.

> * Kernel Space
>
> 1. Providing frames with a future transmit time.  For normal sockets,
>    this can be in the CMESG data.  For mmap'ed buffers, we will need a
>    new format.  (I think Arnd is working on a new layout.)

I need to revisit that discussion again I think.

> 2. Time based qdisc for transmitted frames.  For MACs that support
>    this (like the i210), we only have to place the frame into the
>    correct queue.  For normal HW, we want to be able to reserve a time
>    window in which non-TSN frames are blocked. This is some work, but
>    in the end it should be a generic solution that not only works
>    "perfectly"  with TSN HW but also provides best effort service using
>    any NIC.

Yes, indeed, that would be one good solution, and quite a lot of work.

> 3. ALSA support for tunable AD/DA clocks.  The rate of the Listener's
>    DA clock must match that of the Talker and the other Listeners.

To nitpick a bit, all AD/DAs should match that of the gPTP grandmaster 
(which in most settings would be the Talker). But yes, you need to adjust 
the AD/DA. SRC is slow and painful, best to avoid.

>    Either you adjust it in HW using a VCO or similar, or you do
>    adaptive sample rate conversion in the application. (And that is
>    another reason for *not* having a shared kernel buffer.)  For the
>    Talker, either you adjust the AD clock to match the PTP time, or
>    you measure the frequency offset.

Yes, some hook into adjusting the clock is needed, I wonder if this is 
possible via V4L2, or of the monitor-world is a completely different beast.

> 4. ALSA support for time triggered playback.  The patch series
>    completely ignore the critical issue of media clock recovery.  The 
>    Listener must buffer the stream in order to play it exactly at a 
>    specified time. It cannot simply send the stream ASAP to the audio 
>    HW, because some other Listener might need longer.  AFAICT, there is 
>    nothing in ALSA that allows you to say, sample X should be played at 
>    time Y.

Yes, and this requires a lot of change to ALSA (and probably something in 
V4L2 as well?), so before we get to that, perhaps have a set of patches 
that does this best effort and *then* work on getting time-triggered 
playback into the kernel?

Another item that was brought up last round was getting timing-information 
to/from ALSA, See driver/media/avb/avb_alsa.c, as a start it updates the 
time for last incoming/outgoing frame so that userspace can get that 
information. Probably buggy as heck :)

* Back to your email from last night*

> You are trying to put tons of code into the kernel that really belongs
> in user space, and at the same time, you omit critical functions that
> only the kernel can provide.

Some (well, to be honest, most) of the of the critical functions that my 
driver omits, are omitted because they require substantial effort to 
implement - and befor there's a need for this, that won't happen. So, 
consider the TSN-driver such a need!

I'd love to use a qdisc that uses a time-triggered transmit, that would 
drop the need for a lot of the stuff in tsn_core.c. The same goes for 
time-triggered playback in media.

> > There are at least one AVB-driver (the AV-part of TSN) in the kernel
> > already.
> 
> And which driver is that?

Ah, a proverbial slip of the changelog, we visited this the last iteration, 
that would be the ravb-driver (which is an AVB capable NIC), but it does 
not include much in the way of AVB-support *In* kernel. Sorry about that!

Since then, the iMX7 from NXP has arrived, and this also has HW-support for 
TSN, but not in the kernel AFAICT.

So, the next issue I plan to tackle, is how I do buffers, the current 
approach where tsn_core allocates memory is on its way out and I'll let the 
shim (which means alsa/v4l2) will provide a buffer. Then I'll start looking 
at qdisc.

Thanks!

-- 
Henrik Austad

[toc] | [prev] | [next] | [standalone]


#1544037

FromRichard Cochran <richardcochran@gmail.com>
Date2016-12-17 21:10 +0100
Message-ID<sPtsS-5NV-9@gated-at.bofh.it>
In reply to#1543944
On Sat, Dec 17, 2016 at 10:05:54AM +0100, Henrik Austad wrote:
> I'm sending out a new set now because, what I have works _fairly_ well for 
> testing and a way to see what you can do with AVB. Using spotify to play 
> music on random machines is quite entertaining.

You have missed the point about TSN entirely.  Unless your demo has
synchronized playback (in the low microsecond range), then it really
is pointless.  We can already play music over the LAN using gstreamer,
without a single kernel change.  Heck, gstreamer even has its own
rudimentary synchronization, so your series is a step backwards.

> And therein lies the problem. It cannot yet be written, so we have to start 
> in *some* end. And as I repeatedly stated in June, I'm at an RFC here, 
> trying to spark some interest and lure other developers in :)

The best way to attract interest is to provide the critical
infrastructure missing in the kernel.  Coding a media player in kernel
space is not very interesting in my view.

> Yes, and this requires a lot of change to ALSA (and probably something in 
> V4L2 as well?), so before we get to that, perhaps have a set of patches 
> that does this best effort and *then* work on getting time-triggered 
> playback into the kernel?

No, we don't need a best effort implementation.  That is gstreamer and Co.

> So, the next issue I plan to tackle, is how I do buffers, the current 
> approach where tsn_core allocates memory is on its way out and I'll let the 
> shim (which means alsa/v4l2) will provide a buffer. Then I'll start looking 
> at qdisc.

More than once you wrote something like, "I know that's needed, but it
is just too hard ATM."  Please start with qdisc and tc.  That
shouldn't be too hard.  If we had the AVB shaping rules with one or
two drivers supporting them, that would be one piece already done.

Thanks,
Richard

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web