1 /* SPDX-License-Identifier: GPL-2.0+ */
2 /*
3  * SSH message builder functions.
4  *
5  * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
6  */
7 
8 #ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
9 #define _SURFACE_AGGREGATOR_SSH_MSGB_H
10 
11 #include <asm/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  */
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  */
50 static inline size_t msgb_bytes_used(const struct msgbuf *msgb)
51 {
52 	return msgb->ptr - msgb->begin;
53 }
54 
55 static inline void __msgb_push_u8(struct msgbuf *msgb, u8 value)
56 {
57 	*msgb->ptr = value;
58 	msgb->ptr += sizeof(u8);
59 }
60 
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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 (out). */
193 	__msgb_push_u8(msgb, 0x00);			/* Target ID (in). */
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