1  /* SPDX-License-Identifier: GPL-2.0+ */
2  /*
3   * SSH message builder functions.
4   *
5   * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com>
6   */
7  
8  #ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
9  #define _SURFACE_AGGREGATOR_SSH_MSGB_H
10  
11  #include <linux/unaligned.h>
12  #include <linux/types.h>
13  
14  #include <linux/surface_aggregator/controller.h>
15  #include <linux/surface_aggregator/serial_hub.h>
16  
17  /**
18   * struct msgbuf - Buffer struct to construct SSH messages.
19   * @begin: Pointer to the beginning of the allocated buffer space.
20   * @end:   Pointer to the end (one past last element) of the allocated buffer
21   *         space.
22   * @ptr:   Pointer to the first free element in the buffer.
23   */
24  struct msgbuf {
25  	u8 *begin;
26  	u8 *end;
27  	u8 *ptr;
28  };
29  
30  /**
31   * msgb_init() - Initialize the given message buffer struct.
32   * @msgb: The buffer struct to initialize
33   * @ptr:  Pointer to the underlying memory by which the buffer will be backed.
34   * @cap:  Size of the underlying memory.
35   *
36   * Initialize the given message buffer struct using the provided memory as
37   * backing.
38   */
msgb_init(struct msgbuf * msgb,u8 * ptr,size_t cap)39  static inline void msgb_init(struct msgbuf *msgb, u8 *ptr, size_t cap)
40  {
41  	msgb->begin = ptr;
42  	msgb->end = ptr + cap;
43  	msgb->ptr = ptr;
44  }
45  
46  /**
47   * msgb_bytes_used() - Return the current number of bytes used in the buffer.
48   * @msgb: The message buffer.
49   */
msgb_bytes_used(const struct msgbuf * msgb)50  static inline size_t msgb_bytes_used(const struct msgbuf *msgb)
51  {
52  	return msgb->ptr - msgb->begin;
53  }
54  
__msgb_push_u8(struct msgbuf * msgb,u8 value)55  static inline void __msgb_push_u8(struct msgbuf *msgb, u8 value)
56  {
57  	*msgb->ptr = value;
58  	msgb->ptr += sizeof(u8);
59  }
60  
__msgb_push_u16(struct msgbuf * msgb,u16 value)61  static inline void __msgb_push_u16(struct msgbuf *msgb, u16 value)
62  {
63  	put_unaligned_le16(value, msgb->ptr);
64  	msgb->ptr += sizeof(u16);
65  }
66  
67  /**
68   * msgb_push_u16() - Push a u16 value to the buffer.
69   * @msgb:  The message buffer.
70   * @value: The value to push to the buffer.
71   */
msgb_push_u16(struct msgbuf * msgb,u16 value)72  static inline void msgb_push_u16(struct msgbuf *msgb, u16 value)
73  {
74  	if (WARN_ON(msgb->ptr + sizeof(u16) > msgb->end))
75  		return;
76  
77  	__msgb_push_u16(msgb, value);
78  }
79  
80  /**
81   * msgb_push_syn() - Push SSH SYN bytes to the buffer.
82   * @msgb: The message buffer.
83   */
msgb_push_syn(struct msgbuf * msgb)84  static inline void msgb_push_syn(struct msgbuf *msgb)
85  {
86  	msgb_push_u16(msgb, SSH_MSG_SYN);
87  }
88  
89  /**
90   * msgb_push_buf() - Push raw data to the buffer.
91   * @msgb: The message buffer.
92   * @buf:  The data to push to the buffer.
93   * @len:  The length of the data to push to the buffer.
94   */
msgb_push_buf(struct msgbuf * msgb,const u8 * buf,size_t len)95  static inline void msgb_push_buf(struct msgbuf *msgb, const u8 *buf, size_t len)
96  {
97  	msgb->ptr = memcpy(msgb->ptr, buf, len) + len;
98  }
99  
100  /**
101   * msgb_push_crc() - Compute CRC and push it to the buffer.
102   * @msgb: The message buffer.
103   * @buf:  The data for which the CRC should be computed.
104   * @len:  The length of the data for which the CRC should be computed.
105   */
msgb_push_crc(struct msgbuf * msgb,const u8 * buf,size_t len)106  static inline void msgb_push_crc(struct msgbuf *msgb, const u8 *buf, size_t len)
107  {
108  	msgb_push_u16(msgb, ssh_crc(buf, len));
109  }
110  
111  /**
112   * msgb_push_frame() - Push a SSH message frame header to the buffer.
113   * @msgb: The message buffer
114   * @ty:   The type of the frame.
115   * @len:  The length of the payload of the frame.
116   * @seq:  The sequence ID of the frame/packet.
117   */
msgb_push_frame(struct msgbuf * msgb,u8 ty,u16 len,u8 seq)118  static inline void msgb_push_frame(struct msgbuf *msgb, u8 ty, u16 len, u8 seq)
119  {
120  	u8 *const begin = msgb->ptr;
121  
122  	if (WARN_ON(msgb->ptr + sizeof(struct ssh_frame) > msgb->end))
123  		return;
124  
125  	__msgb_push_u8(msgb, ty);	/* Frame type. */
126  	__msgb_push_u16(msgb, len);	/* Frame payload length. */
127  	__msgb_push_u8(msgb, seq);	/* Frame sequence ID. */
128  
129  	msgb_push_crc(msgb, begin, msgb->ptr - begin);
130  }
131  
132  /**
133   * msgb_push_ack() - Push a SSH ACK frame to the buffer.
134   * @msgb: The message buffer
135   * @seq:  The sequence ID of the frame/packet to be ACKed.
136   */
msgb_push_ack(struct msgbuf * msgb,u8 seq)137  static inline void msgb_push_ack(struct msgbuf *msgb, u8 seq)
138  {
139  	/* SYN. */
140  	msgb_push_syn(msgb);
141  
142  	/* ACK-type frame + CRC. */
143  	msgb_push_frame(msgb, SSH_FRAME_TYPE_ACK, 0x00, seq);
144  
145  	/* Payload CRC (ACK-type frames do not have a payload). */
146  	msgb_push_crc(msgb, msgb->ptr, 0);
147  }
148  
149  /**
150   * msgb_push_nak() - Push a SSH NAK frame to the buffer.
151   * @msgb: The message buffer
152   */
msgb_push_nak(struct msgbuf * msgb)153  static inline void msgb_push_nak(struct msgbuf *msgb)
154  {
155  	/* SYN. */
156  	msgb_push_syn(msgb);
157  
158  	/* NAK-type frame + CRC. */
159  	msgb_push_frame(msgb, SSH_FRAME_TYPE_NAK, 0x00, 0x00);
160  
161  	/* Payload CRC (ACK-type frames do not have a payload). */
162  	msgb_push_crc(msgb, msgb->ptr, 0);
163  }
164  
165  /**
166   * msgb_push_cmd() - Push a SSH command frame with payload to the buffer.
167   * @msgb: The message buffer.
168   * @seq:  The sequence ID (SEQ) of the frame/packet.
169   * @rqid: The request ID (RQID) of the request contained in the frame.
170   * @rqst: The request to wrap in the frame.
171   */
msgb_push_cmd(struct msgbuf * msgb,u8 seq,u16 rqid,const struct ssam_request * rqst)172  static inline void msgb_push_cmd(struct msgbuf *msgb, u8 seq, u16 rqid,
173  				 const struct ssam_request *rqst)
174  {
175  	const u8 type = SSH_FRAME_TYPE_DATA_SEQ;
176  	u8 *cmd;
177  
178  	/* SYN. */
179  	msgb_push_syn(msgb);
180  
181  	/* Command frame + CRC. */
182  	msgb_push_frame(msgb, type, sizeof(struct ssh_command) + rqst->length, seq);
183  
184  	/* Frame payload: Command struct + payload. */
185  	if (WARN_ON(msgb->ptr + sizeof(struct ssh_command) > msgb->end))
186  		return;
187  
188  	cmd = msgb->ptr;
189  
190  	__msgb_push_u8(msgb, SSH_PLD_TYPE_CMD);		/* Payload type. */
191  	__msgb_push_u8(msgb, rqst->target_category);	/* Target category. */
192  	__msgb_push_u8(msgb, rqst->target_id);		/* Target ID. */
193  	__msgb_push_u8(msgb, SSAM_SSH_TID_HOST);	/* Source ID. */
194  	__msgb_push_u8(msgb, rqst->instance_id);	/* Instance ID. */
195  	__msgb_push_u16(msgb, rqid);			/* Request ID. */
196  	__msgb_push_u8(msgb, rqst->command_id);		/* Command ID. */
197  
198  	/* Command payload. */
199  	msgb_push_buf(msgb, rqst->payload, rqst->length);
200  
201  	/* CRC for command struct + payload. */
202  	msgb_push_crc(msgb, cmd, msgb->ptr - cmd);
203  }
204  
205  #endif /* _SURFACE_AGGREGATOR_SSH_MSGB_H */
206