1 /*
2  * Copyright 2019 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "tfa_device_hal.h"
9 #include "usb.h"
10 #include "usb_device_descriptor.h"
11 
12 /*******************************************************************************
13  * Variables
14  *******************************************************************************/
15 tfa_hal_msg_status_t g_TfaDeviceMessageStatus = {.msgComplete = 0, .totalSend = 0};
16 
17 /*******************************************************************************
18  * Code
19  ******************************************************************************/
20 
21 /*!
22  * @brief Collect a message chuck into a internal buffer.
23  *
24  * @param handle TFA9XXX handle structure.
25  * @param chunk The message chunk.
26  * @param length Length of the message to be collected.
27  * @return uint8_t returns 1 when a complete message is collected, returns 0 when there is still remaining to be
28  * collected.
29  */
TFA_Hal_CollectMsg(tfa9xxx_handle_t * handle,uint8_t * chunk,uint32_t length)30 uint8_t TFA_Hal_CollectMsg(tfa9xxx_handle_t *handle, uint8_t *chunk, uint32_t length)
31 {
32     static uint8_t rcvBuffer[TFA_BUFFERSIZE];
33     struct _tfa_hal_msg *msg = (struct _tfa_hal_msg *)rcvBuffer;
34     static uint32_t totalRcv = 0;
35     uint8_t *prcv            = &rcvBuffer[totalRcv];
36 
37     if (length > USB_HID_GENERIC_OUT_BUFFER_LENGTH)
38         return 0;
39 
40     // collect msg chunks
41     memcpy(prcv, chunk, length);
42     totalRcv += length;
43     prcv += length;
44     if (totalRcv < sizeof(struct _tfa_hal_msg))
45     {
46         // TODO validate msg here
47         // get the rest, if needed
48         return 0;
49     }
50     else if (totalRcv < sizeof(struct _tfa_hal_msg) + msg->wlength)
51     {
52         return 0;
53     }
54     else
55     {
56         memcpy(g_TfaDeviceMessageStatus.sndBuffer, msg, totalRcv);
57         TFA_Hal_ProcessMsg(handle, msg, g_TfaDeviceMessageStatus.sndBuffer);
58         totalRcv                             = 0;
59         g_TfaDeviceMessageStatus.totalSend   = USB_HID_GENERIC_OUT_BUFFER_LENGTH; // TODO errorcheck
60         g_TfaDeviceMessageStatus.msgComplete = 1;
61         return 1;
62     }
63 
64     return 0;
65 }
66 
67 /*!
68  * @brief Process the message collected.
69  *
70  * @param handle TFA9XXX handle structure.
71  * @param inBuffer Input message buffer.
72  * @param outBuffer Output (return) message buffer.
73  * @retval 0 Message is processed successfully.
74  * @retval EIO I2C error.
75  * @retval EINVAL Message CRC check failed or command inside the message is invalid.
76  */
TFA_Hal_ProcessMsg(tfa9xxx_handle_t * handle,void * inBuffer,void * outBuffer)77 int32_t TFA_Hal_ProcessMsg(tfa9xxx_handle_t *handle, void *inBuffer, void *outBuffer)
78 {
79     struct _tfa_hal_msg *msg  = (struct _tfa_hal_msg *)inBuffer;
80     struct _tfa_hal_msg *rmsg = (struct _tfa_hal_msg *)outBuffer;
81     int32_t ret;
82     uint32_t msgSize;
83     uint8_t address = 0;
84 
85 #ifdef TFA_HAL_DEBUG
86     TFA_Hal_DumpMsg(msg, 1);
87 #endif
88 
89     /* crc32 check of arrived message, only a write message contains a payload */
90     msgSize = (int16_t)msg->wlength + sizeof(struct _tfa_hal_msg);
91     ret     = TFA_Hal_CheckMsgCRC(msg, msgSize);
92     memcpy(rmsg, msg, sizeof(struct _tfa_hal_msg));
93 
94     if (ret >= 0)
95     {
96         switch (msg->cmd)
97         {
98             case TFA_HAL_MSG_I2C:
99             {
100                 address          = msg->data[0] >> 1;
101                 uint8_t *in_buf  = &msg->data[1];
102                 size_t in_bytes  = msg->wlength - 1;
103                 uint8_t *out_buf = rmsg->data;
104                 size_t out_bytes = rmsg->rlength;
105                 ret = TFA_I2C_WriteReadRaw(handle, address, in_bytes, in_buf, out_bytes, out_buf) ? -EIO : 0;
106             }
107             break;
108             case TFA_HAL_MSG_VERSION:
109                 break;
110             default:
111                 /* Unsupported command */
112                 ret = -EINVAL;
113                 break;
114         }
115     }
116 
117     /* set return message ID + server error code */
118     rmsg->cmd_lsb = 'r';
119     rmsg->error   = ret;
120 
121     /* add crc32 to outgoing message, only a read contains extra bytes */
122     msgSize = (int16_t)msg->rlength + sizeof(struct _tfa_hal_msg);
123 
124     TFA_Hal_AddMsgCRC(rmsg, msgSize);
125 
126 #ifdef TFA_HAL_DEBUG
127     TFA_Hal_DumpMsg(rmsg, 0);
128 #endif
129 
130     return ret;
131 }
132 
133 /*!
134  * @brief Get the length of the next message chunk for the device to sent. If the length is 0, it means the whole
135  * message has been sent.
136  *
137  * @param maxChunkLength Max length to be sent.
138  * @return uint32_t The length of next message chunk to be sent.
139  */
TFA_Hal_GetNextChunkLength(uint32_t maxChunkLength)140 uint32_t TFA_Hal_GetNextChunkLength(uint32_t maxChunkLength)
141 {
142     struct _tfa_hal_msg *rmsg = (struct _tfa_hal_msg *)(g_TfaDeviceMessageStatus.sndBuffer);
143     int32_t remaining         = rmsg->rlength + sizeof(struct _tfa_hal_msg) - g_TfaDeviceMessageStatus.totalSend;
144     uint32_t chunkLength      = remaining > maxChunkLength ? maxChunkLength : remaining;
145 
146     if (chunkLength)
147     {
148         g_TfaDeviceMessageStatus.totalSend += chunkLength;
149     }
150     else
151     {
152         g_TfaDeviceMessageStatus.totalSend = 0; // done
153     }
154 
155     // return max or the tail
156     return chunkLength;
157 }
158 
159 /*!
160  * @brief I2C write read function. It's a wrapper over tfa9xxx driver I2C function.
161  *
162  * @param handle TFA9XXX handle structure.
163  * @param slave Slave address.
164  * @param wrLength Length of data to write.
165  * @param wrData Data to write.
166  * @param rdLength Length of data to read.
167  * @param rdData Pointer to read data buffer.
168  * @return int32_t It returns kStatus_Success if I2C operation finishes successfully. It returns negative value of
169  * _lpi2c_status error code if I2C operation fails.
170  */
TFA_I2C_WriteReadRaw(tfa9xxx_handle_t * handle,uint8_t slave,uint32_t wrLength,uint8_t * wrData,uint32_t rdLength,uint8_t * rdData)171 int32_t TFA_I2C_WriteReadRaw(
172     tfa9xxx_handle_t *handle, uint8_t slave, uint32_t wrLength, uint8_t *wrData, uint32_t rdLength, uint8_t *rdData)
173 {
174     struct i2c_client i2cClient = {
175         .addr = slave,
176         .hal  = &(handle->i2cHandle),
177     };
178     return tfa2_i2c_write_read_raw(&i2cClient, wrLength, wrData, rdLength, rdData);
179 }
180 
181 /*!
182  * @brief I2C write only function to control TFA. It's a wrapper over TFA_I2C_WriteReadRaw().
183  *
184  * @param handle TFA9XXX handle structure.
185  * @param slave Slave address
186  * @param length Length of data to write.
187  * @param data Data to write.
188  * @return int32_t
189  */
TFA_I2C_WriteRaw(tfa9xxx_handle_t * handle,uint8_t slave,uint32_t len,const uint8_t * data)190 int32_t TFA_I2C_WriteRaw(tfa9xxx_handle_t *handle, uint8_t slave, uint32_t len, const uint8_t *data)
191 {
192     return TFA_I2C_WriteReadRaw(handle, slave, len, (uint8_t *)data, 0, NULL);
193 }
194 
195 /*!
196  * @brief Display the data in hex with certain length.
197  *
198  * @param str str to print
199  * @param data data to print
200  * @param length data size
201  */
TFA_Hal_DumpHex(char * str,uint8_t * data,uint32_t length)202 void TFA_Hal_DumpHex(char *str, uint8_t *data, uint32_t length)
203 {
204     int i;
205 
206     if (str == NULL)
207         str = "";
208 
209     TFA9XXX_Printf("%s [%d]:", str, length);
210     for (i = 0; i < length; i++)
211     {
212         TFA9XXX_Printf("%02x ", data[i]);
213     }
214     TFA9XXX_Printf("\n\r");
215 }
216 
217 /*!
218  * @brief Display the contents of the message buffer.
219  *
220  * @param msg Message.
221  * @param rcv Received message or sent message.
222  */
TFA_Hal_DumpMsg(struct _tfa_hal_msg * msg,uint8_t rcv)223 void TFA_Hal_DumpMsg(struct _tfa_hal_msg *msg, uint8_t rcv)
224 {
225     /* header */
226     TFA9XXX_Printf(
227         "cmd(1) = %c, cmd_lsb(1) = 0x%x, seq(2) = %d, index(4) = %d, crc32(4) = 0x%x, error(2) = 0x%x, wlength(2) = "
228         "%d, rlength(2) = %d",
229         msg->cmd, msg->cmd_lsb, msg->seq, msg->index, msg->crc32, msg->error, msg->wlength, msg->rlength);
230 
231     /* data depends in rcv=write or sdn=read */
232     if (rcv)
233         TFA_Hal_DumpHex(", wdata", (uint8_t *)&msg->data, msg->wlength);
234     else
235         TFA_Hal_DumpHex(", rdata", (uint8_t *)&msg->data, msg->rlength);
236     TFA9XXX_Printf("\n");
237 }
238 
TFA_Hal_CheckMsgCRC(struct _tfa_hal_msg * msg,uint32_t msgSize)239 int32_t TFA_Hal_CheckMsgCRC(struct _tfa_hal_msg *msg, uint32_t msgSize)
240 {
241     uint8_t *base;
242     uint32_t size;
243     uint32_t crc_in = 0;
244 
245     /* store, calculate and compare crc32 */
246     crc_in     = msg->crc32;
247     msg->crc32 = 0x0;
248     base       = (uint8_t *)msg;
249     size       = msgSize;
250 
251     if (crc_in != ~crc32_le(~0u, base, size))
252         return -EINVAL; // EPROTO;
253 
254     return 0;
255 }
256 
257 /*!
258  * @brief Calculate CRC of the message and add it to the message structure.
259  *
260  * @param msg Message.
261  * @param msgSize Message size.
262  */
TFA_Hal_AddMsgCRC(struct _tfa_hal_msg * msg,uint32_t msgSize)263 void TFA_Hal_AddMsgCRC(struct _tfa_hal_msg *msg, uint32_t msgSize)
264 {
265     uint8_t *base;
266     uint32_t size;
267 
268     msg->crc32 = 0x0;
269     base       = (uint8_t *)msg;
270     size       = msgSize;
271     msg->crc32 = ~crc32_le(~0u, base, size);
272 
273     return;
274 }
275