| /* SPDX-License-Identifier: GPL-2.0+ */ | |
| /* | |
| * SSH packet transport layer. | |
| * | |
| * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> | |
| */ | |
| #ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H | |
| #define _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H | |
| #include <linux/atomic.h> | |
| #include <linux/kfifo.h> | |
| #include <linux/ktime.h> | |
| #include <linux/list.h> | |
| #include <linux/serdev.h> | |
| #include <linux/spinlock.h> | |
| #include <linux/types.h> | |
| #include <linux/wait.h> | |
| #include <linux/workqueue.h> | |
| #include <linux/surface_aggregator/serial_hub.h> | |
| #include "ssh_parser.h" | |
| /** | |
| * enum ssh_ptl_state_flags - State-flags for &struct ssh_ptl. | |
| * | |
| * @SSH_PTL_SF_SHUTDOWN_BIT: | |
| * Indicates that the packet transport layer has been shut down or is | |
| * being shut down and should not accept any new packets/data. | |
| */ | |
| enum ssh_ptl_state_flags { | |
| SSH_PTL_SF_SHUTDOWN_BIT, | |
| }; | |
| /** | |
| * struct ssh_ptl_ops - Callback operations for packet transport layer. | |
| * @data_received: Function called when a data-packet has been received. Both, | |
| * the packet layer on which the packet has been received and | |
| * the packet's payload data are provided to this function. | |
| */ | |
| struct ssh_ptl_ops { | |
| void (*data_received)(struct ssh_ptl *p, const struct ssam_span *data); | |
| }; | |
| /** | |
| * struct ssh_ptl - SSH packet transport layer. | |
| * @serdev: Serial device providing the underlying data transport. | |
| * @state: State(-flags) of the transport layer. | |
| * @queue: Packet submission queue. | |
| * @queue.lock: Lock for modifying the packet submission queue. | |
| * @queue.head: List-head of the packet submission queue. | |
| * @pending: Set/list of pending packets. | |
| * @pending.lock: Lock for modifying the pending set. | |
| * @pending.head: List-head of the pending set/list. | |
| * @pending.count: Number of currently pending packets. | |
| * @tx: Transmitter subsystem. | |
| * @tx.running: Flag indicating (desired) transmitter thread state. | |
| * @tx.thread: Transmitter thread. | |
| * @tx.thread_cplt_tx: Completion for transmitter thread waiting on transfer. | |
| * @tx.thread_cplt_pkt: Completion for transmitter thread waiting on packets. | |
| * @tx.packet_wq: Waitqueue-head for packet transmit completion. | |
| * @rx: Receiver subsystem. | |
| * @rx.thread: Receiver thread. | |
| * @rx.wq: Waitqueue-head for receiver thread. | |
| * @rx.fifo: Buffer for receiving data/pushing data to receiver thread. | |
| * @rx.buf: Buffer for evaluating data on receiver thread. | |
| * @rx.blocked: List of recent/blocked sequence IDs to detect retransmission. | |
| * @rx.blocked.seqs: Array of blocked sequence IDs. | |
| * @rx.blocked.offset: Offset indicating where a new ID should be inserted. | |
| * @rtx_timeout: Retransmission timeout subsystem. | |
| * @rtx_timeout.lock: Lock for modifying the retransmission timeout reaper. | |
| * @rtx_timeout.timeout: Timeout interval for retransmission. | |
| * @rtx_timeout.expires: Time specifying when the reaper work is next scheduled. | |
| * @rtx_timeout.reaper: Work performing timeout checks and subsequent actions. | |
| * @ops: Packet layer operations. | |
| */ | |
| struct ssh_ptl { | |
| struct serdev_device *serdev; | |
| unsigned long state; | |
| struct { | |
| spinlock_t lock; | |
| struct list_head head; | |
| } queue; | |
| struct { | |
| spinlock_t lock; | |
| struct list_head head; | |
| atomic_t count; | |
| } pending; | |
| struct { | |
| atomic_t running; | |
| struct task_struct *thread; | |
| struct completion thread_cplt_tx; | |
| struct completion thread_cplt_pkt; | |
| struct wait_queue_head packet_wq; | |
| } tx; | |
| struct { | |
| struct task_struct *thread; | |
| struct wait_queue_head wq; | |
| struct kfifo fifo; | |
| struct sshp_buf buf; | |
| struct { | |
| u16 seqs[8]; | |
| u16 offset; | |
| } blocked; | |
| } rx; | |
| struct { | |
| spinlock_t lock; | |
| ktime_t timeout; | |
| ktime_t expires; | |
| struct delayed_work reaper; | |
| } rtx_timeout; | |
| struct ssh_ptl_ops ops; | |
| }; | |
| #define __ssam_prcond(func, p, fmt, ...) \ | |
| do { \ | |
| typeof(p) __p = (p); \ | |
| \ | |
| if (__p) \ | |
| func(__p, fmt, ##__VA_ARGS__); \ | |
| } while (0) | |
| #define ptl_dbg(p, fmt, ...) dev_dbg(&(p)->serdev->dev, fmt, ##__VA_ARGS__) | |
| #define ptl_info(p, fmt, ...) dev_info(&(p)->serdev->dev, fmt, ##__VA_ARGS__) | |
| #define ptl_warn(p, fmt, ...) dev_warn(&(p)->serdev->dev, fmt, ##__VA_ARGS__) | |
| #define ptl_err(p, fmt, ...) dev_err(&(p)->serdev->dev, fmt, ##__VA_ARGS__) | |
| #define ptl_dbg_cond(p, fmt, ...) __ssam_prcond(ptl_dbg, p, fmt, ##__VA_ARGS__) | |
| #define to_ssh_ptl(ptr, member) \ | |
| container_of(ptr, struct ssh_ptl, member) | |
| int ssh_ptl_init(struct ssh_ptl *ptl, struct serdev_device *serdev, | |
| struct ssh_ptl_ops *ops); | |
| void ssh_ptl_destroy(struct ssh_ptl *ptl); | |
| /** | |
| * ssh_ptl_get_device() - Get device associated with packet transport layer. | |
| * @ptl: The packet transport layer. | |
| * | |
| * Return: Returns the device on which the given packet transport layer builds | |
| * upon. | |
| */ | |
| static inline struct device *ssh_ptl_get_device(struct ssh_ptl *ptl) | |
| { | |
| return ptl->serdev ? &ptl->serdev->dev : NULL; | |
| } | |
| int ssh_ptl_tx_start(struct ssh_ptl *ptl); | |
| int ssh_ptl_tx_stop(struct ssh_ptl *ptl); | |
| int ssh_ptl_rx_start(struct ssh_ptl *ptl); | |
| int ssh_ptl_rx_stop(struct ssh_ptl *ptl); | |
| void ssh_ptl_shutdown(struct ssh_ptl *ptl); | |
| int ssh_ptl_submit(struct ssh_ptl *ptl, struct ssh_packet *p); | |
| void ssh_ptl_cancel(struct ssh_packet *p); | |
| ssize_t ssh_ptl_rx_rcvbuf(struct ssh_ptl *ptl, const u8 *buf, size_t n); | |
| /** | |
| * ssh_ptl_tx_wakeup_transfer() - Wake up packet transmitter thread for | |
| * transfer. | |
| * @ptl: The packet transport layer. | |
| * | |
| * Wakes up the packet transmitter thread, notifying it that the underlying | |
| * transport has more space for data to be transmitted. If the packet | |
| * transport layer has been shut down, calls to this function will be ignored. | |
| */ | |
| static inline void ssh_ptl_tx_wakeup_transfer(struct ssh_ptl *ptl) | |
| { | |
| if (test_bit(SSH_PTL_SF_SHUTDOWN_BIT, &ptl->state)) | |
| return; | |
| complete(&ptl->tx.thread_cplt_tx); | |
| } | |
| void ssh_packet_init(struct ssh_packet *packet, unsigned long type, | |
| u8 priority, const struct ssh_packet_ops *ops); | |
| int ssh_ctrl_packet_cache_init(void); | |
| void ssh_ctrl_packet_cache_destroy(void); | |
| #endif /* _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H */ |