blob: d2a6b9a45a752ca2f1d6958aa7c69c6a4c51a06b [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2018 MediaTek Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#if !defined(__KDUMP_USB_C__)
25#define __KDUMP_USB_C__
26
27#include <mrdump.h>
28#include <malloc.h>
29#include <printf.h>
30#include <string.h>
31#include <sys/types.h>
32#include "aee.h"
33#include "kdump.h"
34
35/*
36 * USB Connectivity
37 * Note: for usb transmission
38 * QMU mode : MAX packet length: 63x1024 = 64512 byte. -> GPD_BUF_SIZE_ALIGN
39 * ZLP issue: EXSPACE should not be multiple size of 512 byte.
40 */
41#define EXSPACE 64256
42#define MAX_RSP_SIZE 64
43#define CON_TIMEOUT 5000
44
45/* Flow control */
46#define USB_RTS "_RTS"
47#define USB_CTS "_CTS"
48#define USB_FIN "_FIN"
49#define USBDONE "DONE"
50
51/*
52 * Need to align 64 bytes to meet cache line length
53 * 1. cmd[MAX_RSP_SIZE]
54 * 2. data[EXSPACE]
55 */
56struct mrdump_usb_handle {
57 char cmd[MAX_RSP_SIZE];
58 uint8_t data[EXSPACE];
59 unsigned int zipsize;
60 int idx;
61};
62
63/* flow control of usb connection */
64static bool usb_data_transfer(struct mrdump_usb_handle *handle, int length)
65{
66 int len;
67
68 /* send RTS */
69 memset(handle->cmd, 0, MAX_RSP_SIZE);
70 len = snprintf(handle->cmd, MAX_RSP_SIZE, "%s", USB_RTS);
71 len = usb_write_with_timeout(handle->cmd, strlen(handle->cmd), CON_TIMEOUT);
72 if (len > 0) {
73
74 /* receive CTS */
75 memset(handle->cmd, 0, MAX_RSP_SIZE);
76 len = usb_read_with_timeout(handle->cmd, MAX_RSP_SIZE, CON_TIMEOUT);
77 if ((len == (int)strlen(USB_CTS))&&
78 (!strncmp(handle->cmd, USB_CTS, strlen(USB_CTS)))) {
79
80 /* send DATA */
81 len = usb_write_with_timeout(handle->data, length, CON_TIMEOUT);
82 if (len > 0) {
83
84 /* get FIN */
85 memset(handle->cmd, 0, sizeof(handle->cmd));
86 len = usb_read_with_timeout(handle->cmd, sizeof(handle->cmd), CON_TIMEOUT);
87 if ((len == (int)strlen(USB_FIN)) &&
88 (!strncmp(handle->cmd, USB_FIN, strlen(USB_FIN)))) {
89 return true;
90 } else {
91 voprintf_error("%s: failed to get FIN.cmd<%p,%d><%x,%x,%x,%x>\n", __func__,
92 handle->cmd, len, handle->cmd[0], handle->cmd[1], handle->cmd[2], handle->cmd[3]);
93 }
94
95 } else {
96 voprintf_error("%s: send DATA error.\n", __func__);
97 }
98 } else {
99 voprintf_error("%s: Not CTS after RTS.cmd<%p,%d><%x,%x,%x,%x>\n", __func__,
100 handle->cmd, len, handle->cmd[0], handle->cmd[1], handle->cmd[2], handle->cmd[3]);
101 }
102 } else {
103 voprintf_error("%s: send RTS error.\n", __func__);
104 }
105 return false;
106}
107
108/* store data in pool (EXSPACE) and write when pool is full */
109static int do_store_or_write(struct mrdump_usb_handle *handle, uint8_t *buf, uint32_t length)
110{
111 int total;
112 unsigned int leftspace, mylen, reval;
113
114
115 /* count for leftspace */
116 total = EXSPACE;
117 leftspace = total - handle->idx;
118
119 /* check length */
120 if (length > leftspace) {
121 mylen = leftspace;
122 reval = length - leftspace;
123 } else {
124 mylen = length;
125 reval = 0;
126 }
127
128 /* store */
129 while (mylen > 0) {
130 handle->data[handle->idx] = *buf;
131 handle->idx++;
132 buf++;
133 mylen--;
134 }
135
136 /* write */
137 if (handle->idx == total) {
138 if (!usb_data_transfer(handle, handle->idx)) {
139 voprintf_error("%s: connection failed.(error idx: %d)\n",
140 __func__, handle->idx);
141 return -1;
142 }
143 handle->idx = 0;
144 }
145
146 return reval;
147}
148
149static int usb_write_cb(void *opaque_handle, void *buf, int size)
150{
151 unsigned int len, moves;
152 int ret = 0;
153 uint8_t *ptr;
154
155 struct mrdump_usb_handle *handle = opaque_handle;
156
157 handle->zipsize += size;
158
159 /* EOF, write the left Data in handle data buffer... */
160 if ((buf == NULL) && (size == 0)) {
161
162 /* MUST: a delay for the last transmission */
163 mdelay(10);
164
165 if (!usb_data_transfer(handle, handle->idx)) {
166 voprintf_error("%s: connection failed.(error idx: %d)\n",
167 __func__, handle->idx);
168 return -1;
169 }
170
171 /* send "MRDUMP ZLP" */
172 memset((void *)handle->data, 0, sizeof(handle->data));
173 size = snprintf((char *)handle->data, sizeof(handle->data), "%s_%s",
174 MRDUMP_GO_DUMP, USBDONE);
175 if (0 > usb_write_with_timeout(handle->data, strlen((char *)handle->data),
176 CON_TIMEOUT)) {
177 voprintf_error(" USB Dump: Write MRDUMP ZLP failed.\n");
178 return -1;
179 }
180
181 return 0;
182 }
183
184 /* buf should not be NULL if not EOF */
185 if (buf == NULL)
186 return -1;
187
188 /* process of Store and write */
189 len = size;
190 ptr = (uint8_t *)buf;
191 while (1) {
192 ret = do_store_or_write(handle, ptr, len);
193 if (ret < 0) {
194 voprintf_error(" USB Dump: store and write failed.\n");
195 return -1;
196 } else if (ret == 0) {
197 break;
198 } else {
199 moves = len - ret;
200 ptr += moves;
201 len = ret;
202 }
203 }
204
205 return size;
206}
207
208int kdump_usb_output(const struct mrdump_control_block *mrdump_cb,
209 const struct kzip_addlist *memlist)
210{
211 int i;
212 struct kzip_addlist expdb_file[2];
213 char *expdb_filename;
214 unsigned int expdb_offset, expdb_filesize;
215 voprintf_info("Output by USB\n");
216
217 struct mrdump_usb_handle *handle = memalign(64, sizeof(struct mrdump_usb_handle));
218 if (handle == NULL) {
219 voprintf_error("No enough memory.");
220 return -1;
221 }
222 memset(handle, 0, sizeof(struct mrdump_usb_handle));
223
224 mdelay(100);
225 bool ok = true;
226 mtk_wdt_restart();
227 struct kzip_file *zf = kzip_open(handle, usb_write_cb);
228 if (zf != NULL) {
229 /* add SYS_COREDUMP */
230 if (!kzip_add_file(zf, memlist, "SYS_COREDUMP"))
231 ok = false;
232
233 /* add file on expdb */
234 for (i = 0; i < IPANIC_NR_SECTIONS; i++) {
235 if (kedump_get_data_info(i, &expdb_filename, &expdb_offset, &expdb_filesize) == 0) {
236 expdb_file[0].address = expdb_offset;
237 expdb_file[0].size = expdb_filesize;
238 expdb_file[0].type = EXPDB_FILE;
239 expdb_file[1].address = 0;
240 expdb_file[1].size = 0;
241 expdb_file[1].type = MEM_NO_MAP;
242 if (!kzip_add_file(zf, expdb_file, expdb_filename))
243 ok = false;
244 }
245 }
246
247 /* close zipfile */
248 kzip_close(zf);
249 usb_write_cb(handle, NULL, 0); /* really write the last part */
250 zf = NULL;
251 } else {
252 ok = false;
253 }
254 free(handle);
255
256 mtk_wdt_restart();
257 if (ok) {
258 mrdump_status_ok("OUTPUT:%s\nMODE:%s\n", "USB DUMP",
259 mrdump_mode2string(mrdump_cb->crash_record.reboot_mode));
260 }
261
262 return ok ? 0 : -1;
263}
264
265#endif