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