From: Henrik Austad haustad@cisco.com
This defines the general TSN headers for network packets, the shim-interface and the central 'tsn_list' structure.
Cc: "David S. Miller" davem@davemloft.net Signed-off-by: Henrik Austad haustad@cisco.com --- include/linux/tsn.h | 806 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 806 insertions(+) create mode 100644 include/linux/tsn.h
diff --git a/include/linux/tsn.h b/include/linux/tsn.h new file mode 100644 index 0000000..0e1f732b --- /dev/null +++ b/include/linux/tsn.h @@ -0,0 +1,806 @@ +/* TSN - Time Sensitive Networking + * + * Copyright (C) 2016- Henrik Austad haustad@cisco.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _TSN_H +#define _TSN_H +#include <linux/list.h> +#include <linux/configfs.h> +#include <linux/hrtimer.h> + +/* The naming here can be a bit confusing as we call it TSN but naming + * suggests 'AVB'. Reason: IEE 1722 was written before the working group + * was renamed to Time Sensitive Networking. + * + * To be precise. TSN describes the protocol for shipping data, AVB is a + * medialayer which you can build on top of TSN. + * + * For this reason the frames are given avb-names whereas the functions + * use tsn_-naming. + */ + +/* 7 bit value 0x00 - 0x7F */ +enum avtp_subtype { + AVTP_61883_IIDC = 0, + AVTP_MMA = 0x1, + AVTP_MAAP = 0x7e, + AVTP_EXPERIMENTAL = 0x7f, +}; + +/* NOTE NOTE NOTE !! + * The headers below use bitfields extensively and verifications + * are needed when using little-endian vs big-endian systems. + */ + +/* Common part of avtph header + * + * AVB Transport Protocol Common Header + * + * Defined in 1722-2011 Sec. 5.2 + */ +struct avtp_ch { +#if defined(__LITTLE_ENDIAN_BITFIELD) + /* use avtp_subtype enum. + */ + u8 subtype:7; + + /* Controlframe: 1 + * Dataframe : 0 + */ + u8 cd:1; + + /* Type specific data, part 1 */ + u8 tsd_1:4; + + /* In current version of AVB, only 0 is valid, all other values + * are reserved for future versions. + */ + u8 version:3; + + /* Valid StreamID in frame + * + * ControlData not related to a specific stream should clear + * this (and have stream_id = 0), _all_ other values should set + * this to 1. + */ + u8 sv:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 cd:1; + u8 subtype:7; + u8 sv:1; + u8 version:3; + u8 tsd_1:4; +#else +#error "Unknown Endianness, cannot determine bitfield ordering" +#endif + /* Type specific data (adjacent to tsd_1, but split due to bitfield) */ + u16 tsd_2; + u64 stream_id; + + /* + * payload by subtype + */ + u8 pbs[0]; +} __packed; + +/* AVTPDU Common Control header format + * IEEE 1722#5.3 + */ +struct avtpc_header { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 subtype:7; + u8 cd:1; + u8 control_data:4; + u8 version:3; + u8 sv:1; + u16 control_data_length:11; + u16 status:5; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 cd:1; + u8 subtype:7; + u8 sv:1; + u8 version:3; + u8 control_data:4; + u16 status:5; + u16 control_data_length:11; +#else +#error "Unknown Endianness, cannot determine bitfield ordering" +#endif + u64 stream_id; +} __packed; + +/* AVTP common stream data AVTPDU header format + * IEEE 1722#5.4 + */ +struct avtpdu_header { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 subtype:7; + u8 cd:1; + + /* avtp_timestamp valid */ + u8 tv: 1; + + /* gateway_info valid */ + u8 gv:1; + + /* reserved */ + u8 r:1; + + /* + * Media clock Restart toggle + */ + u8 mr:1; + + u8 version:3; + + /* StreamID valid */ + u8 sv:1; + u8 seqnr; + + /* Timestamp uncertain */ + u8 tu:1; + u8 r2:7; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 cd:1; + u8 subtype:7; + + u8 sv:1; + u8 version:3; + u8 mr:1; + u8 r:1; + u8 gv:1; + u8 tv: 1; + + u8 seqnr; + u8 r2:7; + u8 tu:1; +#else +#error "Unknown Endianness, cannot determine bitfield ordering" +#endif + + u64 stream_id; + + u32 avtp_timestamp; + u32 gateway_info; + + /* Stream Data Length */ + u16 sd_len; + + /* Protocol specific header, derived from avtp_subtype */ + u16 psh; + + /* Stream Payload Data 0 to n octets + * n so that total size < MTU + */ + u8 data[0]; +} __packed; + + +/** + * struct tsn_list - The top level container of TSN + * + * This is what tsn_configfs refers to as 'tier-0' + * + * @head List of TSN cards + * @lock lock protecting global entries + * @tsn_subsys Ref to ConfigFS subsystem + * + * @running: hrtimer is running driving data out + * @tsn_timer: hrtimer container + * @num_avail Number of available TSN NICs exposed through ConfigFS + */ +struct tsn_list { + struct list_head head; + struct mutex lock; + struct configfs_subsystem tsn_subsys; + + /* + * TSN-timer is running. Not to be confused with the per-link + * disabled flag which indicates if a remote client, like aplay, + * is pushing data to it. + */ + atomic_t running; + struct hrtimer tsn_timer; + unsigned int period_ns; + + + size_t num_avail; +}; + +/** + * struct tsn_nic + * + * Individual TSN-capable NICs, or 'tier-1' struct + * + * @list linked list of all TSN NICs + * @group configfs group + * @dev corresponding net_device + * @dma_size : size of the DMA buffer + * @dma_handle: housekeeping DMA-stuff + * @dma_mem : pointer to memory region we're using for DMAing to the NIC + * @name Name of NIC (same as name in dev), TO BE REMOVED + * @txq Size of Tx-queue. TO BE REMOVED + * @rx_registered flag indicating if a handler is registered for the nic + * @capable: if the NIC is capable for proper TSN traffic or if it must + * be emulated in software. + * + */ +struct tsn_nic { + struct list_head list; + struct config_group group; + struct net_device *dev; + struct tsn_list *tsn_list; + + size_t dma_size; + dma_addr_t dma_handle; + void *dma_mem; + + char *name; + int txq; + u8 rx_registered:1; + u8 capable:1; + u8 reserved:6; +}; + +struct tsn_shim_ops; +/** + * tsn_link - Structure describing a single TSN link + * + */ +struct tsn_link { + /* + * Lock for protecting the buffer + */ + spinlock_t lock; + + struct config_group group; + struct tsn_nic *nic; + struct hlist_node node; + + /* The link itself is active, and the tsn_core will treat it as + * an active participant and feed data from it to the + * network. This places some restrictions on which attributes + * can be changed. + * + * 1: active + * 0: inactive + */ + atomic_t active; + + u64 timer_period_ns; + + /* Pointer to media-specific data. + * e.g. struct avb_chip + */ + void *media_chip; + + u64 stream_id; + + /* + * The max required size for a _single_ TSN frame. + * + * To be used instead of channels and sample_freq. + */ + u16 max_payload_size; + u16 shim_header_size; + + /* + * Size of buffer (in bytes) to use when handling data to/from + * NIC. + * + * Smaller size will result in client being called more often + * but also provides lower latencies. + */ + size_t buffer_size; + size_t used_buffer_size; + + /* + * Used when frames are constructed and shipped to the network + * layer. If this is true, 0-frames will be sent insted of data + * from the buffer. + */ + atomic_t buffer_active; + + /* + * ringbuffer for incoming or outging traffic + * +-----------------------------------+ + * | ########## | + * +-----------------------------------+ + * ^ ^ ^ ^ + * buffer tail head end + * + * Buffer: start of memory area + * tail: first byte of data in buffer + * head: first unused slot in which to store new data + * + * head,tail is used to represent the position of 'live data' in + * the buffer. + */ + void *buffer; + void *head; + void *tail; + void *end; + + /* Number of bytes to run refill/drain callbacks */ + size_t low_water_mark; + size_t high_water_mark; + + + /* + * callback ops. + */ + struct tsn_shim_ops *ops; + + /* + * EndStation Type + * + * Either Talker or Listener + * + * 1: We are *Talker*, i.e. producing data to send + * 0: We are *Listener*, i.e. we receive data from another ES. + * + * This is for a single link, so even though an end-station can + * be both Talker *and* Listener, a link can only be one. + */ + u8 estype_talker; + + /* + * Link will use buffer managed by the shim. For this to work, + * the shim must: + * + * - call tsn_use_external_buffer(link, size); + * - provide tsn_shim_buffer_swap(link) in tsn_shim_ops + */ + u8 external_buffer; + + u8 last_seqnr; + + /* + * Class can be either A or B + * + * ClassA: every 125us + * ClassB: every 250us + * + * This will also affect how large each frame will be. + */ + u8 class_a:1; + + /* + * Any AVTP data stream must set the 802.1Q vlan id and priority + * Code point. This should be obtained from MSRP, default values + * are: + * + * pvid: SR_PVID 2 + * pcp: Class A: 3 + * Class B: 2 + * + * See IEEE 802.1Q-2011, Sec 35.2.2.9.3 and table 6-6 in 6.6.2 + * for details + */ + u8 pcp_a:3; + u8 pcp_b:3; + u16 vlan_id:12; + + u8 remote_mac[6]; +}; + +/** + * tsn_link_on - make link active + * + * This cause most of the attributes to be treated read-only since we + * will have to re-negotiate with the network if most of these + * parameters change. + * + * Note: this means that the link will be handled by the rx-handler or + * the timer callback, but until the link_buffer is set active (via + * tsn_lb_on()), actual data is not moved. + * + * @link: link being set to active + */ +static inline void tsn_link_on(struct tsn_link *link) +{ + if (link) + atomic_set(&link->active, 1); +} + +/** + * tsn_link_off - make link inactive + * + * The link will now be ignored by timer callback or the + * rx-handler. Attributes can be mostly freely changed (we assume that + * userspace sets values that are negotiated properly). + * + * @link: link to deactivate + */ +static inline void tsn_link_off(struct tsn_link *link) +{ + if (link) + atomic_set(&link->active, 0); +} + +/** + * tsn_link_is_on - query link to see if it is active + * + * Mostly used by tsn_configfs to respect the "read-only" once link is + * configured and made active. + * + * @link active link + * @returns 1 if active/on, 0 otherwise + */ +static inline int tsn_link_is_on(struct tsn_link *link) +{ + if (link) + return atomic_read(&link->active); + return 0; +} + +/** + * tsn_set_buffer_size - adjust buffersize to match a shim + * + * This will not allocate (or deallcoate) memory, just adjust how much + * of the buffer allocated in tsn_prepare_link is being used. tsn_ + * expects tsn_clear_buffer_size() to be invoked when stream is closed. + */ +int tsn_set_buffer_size(struct tsn_link *link, size_t bsize); +int tsn_clear_buffer_size(struct tsn_link *link); + +/** + * tsn_buffer_write write data into the buffer from shim + * + * This is called from the shim-driver when more data is available and + * data needs to be pushed out to the network. + * + * NOTE: This is used when TSN handles the databuffer. This will not be + * needed for "shim-hosted" buffers. + * + * _If_ this function is called when the link is inactive, it will + * _enable_ the link (i.e. link will mark the buffer as 'active'). Do + * not copy data into the buffer unless you are ready to start sending + * frames! + * + * @link active link + * @src the buffer to copy data from + * @bytes bytes to copy + * @return bytes copied from link->buffer or negative error + */ +int tsn_buffer_write(struct tsn_link *link, void *src, size_t bytes); + + +/** + * tsn_buffer_read - read data from link->buffer and give to shim + * + * When we act as a listener, this is what the shim (should|will) call + * to grab data. It typically grabs much more data than the _net + * equivalent. It also do not trigger a refill-event the same way + * buffer_read_net does. + * + * @param link current link that holds the buffer + * @param buffer the buffer to copy into, must be at least of size bytes + * @param bytes number of bytes. + * + * Note that this routine does NOT CARE about channels, samplesize etc, + * it is a _pure_ copy that handles ringbuffer wraps etc. + * + * This function have side-effects as it will update internal tsn_link + * values. + * + * @return Bytes copied into link->buffer, negative value upon error. + */ +int tsn_buffer_read(struct tsn_link *link, void *buffer, size_t bytes); + +/** + * tsn_lb_enable - TSN Link Buffer Enable + * + * Mark the link as "buffer-enabled" which will let the core start + * shifting data in/out of the buffer instead of ignoring incoming + * frames or sending "nullframes". + * + * This is for the network-end of the tsn-buffer, i.e. + * - when enabled frames *from* the network will be inserted into the buffer, + * - or frames going *out* will include data from the buffer instead of sending + * null-frames. + * + * When disabled, data will be zero'd, e.g Tx will send NULL-frames and + * Rx will silently drop the frames. + * + * @link: active link + */ +static inline void tsn_lb_enable(struct tsn_link *link) +{ + if (link) + atomic_set(&link->buffer_active, 1); +} + +/** + * tsn_lb_disable - stop using the buffer for the net-side of TSN + * + * When we close a stream, we do not necessarily tear down the link, and + * we need to handle the data in some way. + */ +static inline void tsn_lb_disable(struct tsn_link *link) +{ + if (link) + atomic_set(&link->buffer_active, 0); +} + +/** + * tsn_lb() - query if we have disabled pushing of data to/from link-buffer + * + * @param struct tsn_link *link - active link + * @returns 1 if link is enabled + */ +static inline int tsn_lb(struct tsn_link *link) +{ + if (link) + return atomic_read(&link->buffer_active); + + /* if link is NULL; buffer not active */ + return 0; +} + + +/** + * Shim ops - what tsn_core use when calling back into the shim. All ops + * must be reentrant. + */ +#define SHIM_NAME_SIZE 32 +struct tsn_shim_ops { + + /* internal linked list used by tsn_core to keep track of all + * shims. + */ + struct list_head head; + + /** + * name - a unique name identifying this shim + * + * This is what userspace use to indicate to core what SHIM a + * particular link will use. If the name is already present, + * core will reject this name. + */ + char shim_name[SHIM_NAME_SIZE]; + + /** + * probe - callback when a new link of this type is instantiated. + * + * When a new link is brought online, this is called once the + * essential parts of tsn_core has finiesh. Once probe_cb has + * finisehd, the shim _must_ be ready to accept data to/from + * tsn_core. On the other hand, due to the final steps of setup, + * it cannot expect to be called into action immediately after + * probe has finished. + * + * In other words, shim must be ready, but core doesn't have to + * + * @param : a particular link to pass along to the probe-function. + */ + int (*probe)(struct tsn_link *link); + + /** + * buffer_swap - set a new buffer for the link. [OPTIONAL] + * + * Used when external buffering is enabled. + * + * When called, a new buffer must be returned WITHOUT blocking + * as this will be called from interrupt context. + * + * The buffer returned from the shim must be at least the size + * of used_buffer_size. + * + * @param current link + * @param old_buffer the buffer that are no longer needed + * @param used number of bytes in buffer that has been filled with data. + * @return new buffer to use + */ + void * (*buffer_swap)(struct tsn_link *link, void *old_buffer, + size_t used); + + /** + * buffer_refill - signal shim that more data is required + * @link Active link + * + * This function should not do anything that can preempt the + * task (kmalloc, sleeping lock) or invoke actions that can take + * a long time to complete. + * + * This will be called from tsn_buffer_read_net() when available + * data in the buffer drops below low_water_mark. It will be + * called with the link-lock *held* + */ + size_t (*buffer_refill)(struct tsn_link *link); + + /** + * buffer_drain - shim need to copy data from buffer + * + * This will be called from tsn_buffer_write_net() when data in + * the buffer exceeds high_water_mark. + * + * The expected behavior is for the shim to then fill data into + * the buffer via tsn_buffer_write() + */ + size_t (*buffer_drain)(struct tsn_link *link); + + /** + * media_close - shut down media controller properly + * + * when the link is closed/removed for some reason + * external to the media controller (ALSA soundcard, v4l2 driver + * etc), we call this to clean up. + * + * Normal operation is stopped before media_close is called, but + * all references should be valid. TSN core expects media_close + * to handle any local cleanup, once returned, any references in + * stale tsn_links cannot be trusted. + * + * @link: current link where data is stored + * @returns: 0 upon success, negative on error. + */ + int (*media_close)(struct tsn_link *link); + + /** + * hdr_size - ask shim how large the header is + * + * Needed when reserving space in skb for transmitting data. + * + * @link: current link where data is stored + * @return: size of header for this shim + */ + size_t (*hdr_size)(struct tsn_link *link); + + /** + * copy_size - ask client how much from the buffer to include in + * the next frame. + * + * This is for *outgoing* frames, incoming frames + * have 'sd_len' set in the header. + * + * Note: copy_size should not return a size larger + * than link->max_payload_size + */ + size_t (*copy_size)(struct tsn_link *link); + + /** + * validate_header - let the shim validate subtype-header + * + * Both psh and data may (or may not) contain headers that need + * validating. This is the responsibility of the shim to + * validate, and ops->valdiate_header() will be called before + * any data is copied from the incoming frame and into the + * buffer. + * + * Important: tsn_core expects validate_header to _not_ alter + * the contents of the frame, and ideally, validate_header could + * be called multiple times and give the same result. + * + * @param: active link owning the new data + * @param: start of data-unit header + * + * This function will be called from interrupt-context and MUST + * NOT take any locks. + */ + int (*validate_header)(struct tsn_link *link, + struct avtpdu_header *header); + + /** + * assemble_header - add shim-specific headers + * + * This adds the headers required by the current shim after the + * generic 1722-header. + * + * @param: active link + * @param: start of data-unit header + * @param: size of data to send in this frame + * @return void + */ + void (*assemble_header)(struct tsn_link *link, + struct avtpdu_header *header, size_t bytes); + + /** + * get_payload_data - get a pointer to where the data is stored + * + * core will use the pointer (or drop it if NULL is returned) + * and copy header->sd_len bytes of *consecutive* data from the + * target memory and into the buffer memory. + * + * This is called with relevant locks held, from interrupt context. + * + * @param link active link + * @param header header of frame, which contains data + * @returns pointer to memory to copy from + */ + void * (*get_payload_data)(struct tsn_link *link, + struct avtpdu_header *header); +}; +/** + * tsn_shim_register_ops - register shim-callbacks for a given shim + * + * @param shim_ops - callbacks. The ops-struct should be kept intact for + * as long as the driver is running. + * + * + */ +int tsn_shim_register_ops(struct tsn_shim_ops *shim_ops); + +/** + * tsn_shim_deregister_ops - remove callback for module + * + * Completely remove shim_ops. This will close any links currently using + * this shim. Note: the links will be closed, but _not_ removed. + * + * @param shim_ops ops associated with this shim + */ +void tsn_shim_deregister_ops(struct tsn_shim_ops *shim_ops); + +/** + * tsn_shim_get_active : return the name of the currently loaded shim + * + * @param current link + * @return name of shim (matches an entry from exported triggers) + */ +char *tsn_shim_get_active(struct tsn_link *link); + +/** + * tsn_shim_find_by_name find shim_ops by name + * + * @param name of shim + * @return shim or NULL if not found/error. + */ +struct tsn_shim_ops *tsn_shim_find_by_name(const char *name); + +/** + * tsn_shim_export_probe_triggers - export a list of registered shims + * + * @param page to write content into + * @returns length of data written to page + */ +ssize_t tsn_shim_export_probe_triggers(char *page); + +/** + * tsn_get_framesize - get the size of the next TSN frame to send + * + * This will call into the shim to get the next chunk of data to + * read. Some sanitychecking is performed, i.e. + * + * 0 <= size <= max_payload_size + * + * @param struct tsn_link *link active link + * @returns size of frame in bytes or negative on error. + */ +static inline size_t tsn_shim_get_framesize(struct tsn_link *link) +{ + size_t ret; + + ret = link->ops->copy_size(link); + if (ret <= link->max_payload_size) + return ret; + return link->max_payload_size; +} + +/** + * tsn_get_hdr_size - get the size of the shim-specific header size + * + * The shim will add it's own header to the frame. + */ +static inline size_t tsn_shim_get_hdr_size(struct tsn_link *link) +{ + size_t ret; + + if (!link || !link->ops->hdr_size) + return -EINVAL; + ret = link->ops->hdr_size(link); + if (ret > link->max_payload_size) + return -EINVAL; + return ret; +} + +#endif /* _TSN_H */