blob: d2a6b9a45a752ca2f1d6958aa7c69c6a4c51a06b [file] [log] [blame]
/*
* Copyright (c) 2018 MediaTek Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if !defined(__KDUMP_USB_C__)
#define __KDUMP_USB_C__
#include <mrdump.h>
#include <malloc.h>
#include <printf.h>
#include <string.h>
#include <sys/types.h>
#include "aee.h"
#include "kdump.h"
/*
* USB Connectivity
* Note: for usb transmission
* QMU mode : MAX packet length: 63x1024 = 64512 byte. -> GPD_BUF_SIZE_ALIGN
* ZLP issue: EXSPACE should not be multiple size of 512 byte.
*/
#define EXSPACE 64256
#define MAX_RSP_SIZE 64
#define CON_TIMEOUT 5000
/* Flow control */
#define USB_RTS "_RTS"
#define USB_CTS "_CTS"
#define USB_FIN "_FIN"
#define USBDONE "DONE"
/*
* Need to align 64 bytes to meet cache line length
* 1. cmd[MAX_RSP_SIZE]
* 2. data[EXSPACE]
*/
struct mrdump_usb_handle {
char cmd[MAX_RSP_SIZE];
uint8_t data[EXSPACE];
unsigned int zipsize;
int idx;
};
/* flow control of usb connection */
static bool usb_data_transfer(struct mrdump_usb_handle *handle, int length)
{
int len;
/* send RTS */
memset(handle->cmd, 0, MAX_RSP_SIZE);
len = snprintf(handle->cmd, MAX_RSP_SIZE, "%s", USB_RTS);
len = usb_write_with_timeout(handle->cmd, strlen(handle->cmd), CON_TIMEOUT);
if (len > 0) {
/* receive CTS */
memset(handle->cmd, 0, MAX_RSP_SIZE);
len = usb_read_with_timeout(handle->cmd, MAX_RSP_SIZE, CON_TIMEOUT);
if ((len == (int)strlen(USB_CTS))&&
(!strncmp(handle->cmd, USB_CTS, strlen(USB_CTS)))) {
/* send DATA */
len = usb_write_with_timeout(handle->data, length, CON_TIMEOUT);
if (len > 0) {
/* get FIN */
memset(handle->cmd, 0, sizeof(handle->cmd));
len = usb_read_with_timeout(handle->cmd, sizeof(handle->cmd), CON_TIMEOUT);
if ((len == (int)strlen(USB_FIN)) &&
(!strncmp(handle->cmd, USB_FIN, strlen(USB_FIN)))) {
return true;
} else {
voprintf_error("%s: failed to get FIN.cmd<%p,%d><%x,%x,%x,%x>\n", __func__,
handle->cmd, len, handle->cmd[0], handle->cmd[1], handle->cmd[2], handle->cmd[3]);
}
} else {
voprintf_error("%s: send DATA error.\n", __func__);
}
} else {
voprintf_error("%s: Not CTS after RTS.cmd<%p,%d><%x,%x,%x,%x>\n", __func__,
handle->cmd, len, handle->cmd[0], handle->cmd[1], handle->cmd[2], handle->cmd[3]);
}
} else {
voprintf_error("%s: send RTS error.\n", __func__);
}
return false;
}
/* store data in pool (EXSPACE) and write when pool is full */
static int do_store_or_write(struct mrdump_usb_handle *handle, uint8_t *buf, uint32_t length)
{
int total;
unsigned int leftspace, mylen, reval;
/* count for leftspace */
total = EXSPACE;
leftspace = total - handle->idx;
/* check length */
if (length > leftspace) {
mylen = leftspace;
reval = length - leftspace;
} else {
mylen = length;
reval = 0;
}
/* store */
while (mylen > 0) {
handle->data[handle->idx] = *buf;
handle->idx++;
buf++;
mylen--;
}
/* write */
if (handle->idx == total) {
if (!usb_data_transfer(handle, handle->idx)) {
voprintf_error("%s: connection failed.(error idx: %d)\n",
__func__, handle->idx);
return -1;
}
handle->idx = 0;
}
return reval;
}
static int usb_write_cb(void *opaque_handle, void *buf, int size)
{
unsigned int len, moves;
int ret = 0;
uint8_t *ptr;
struct mrdump_usb_handle *handle = opaque_handle;
handle->zipsize += size;
/* EOF, write the left Data in handle data buffer... */
if ((buf == NULL) && (size == 0)) {
/* MUST: a delay for the last transmission */
mdelay(10);
if (!usb_data_transfer(handle, handle->idx)) {
voprintf_error("%s: connection failed.(error idx: %d)\n",
__func__, handle->idx);
return -1;
}
/* send "MRDUMP ZLP" */
memset((void *)handle->data, 0, sizeof(handle->data));
size = snprintf((char *)handle->data, sizeof(handle->data), "%s_%s",
MRDUMP_GO_DUMP, USBDONE);
if (0 > usb_write_with_timeout(handle->data, strlen((char *)handle->data),
CON_TIMEOUT)) {
voprintf_error(" USB Dump: Write MRDUMP ZLP failed.\n");
return -1;
}
return 0;
}
/* buf should not be NULL if not EOF */
if (buf == NULL)
return -1;
/* process of Store and write */
len = size;
ptr = (uint8_t *)buf;
while (1) {
ret = do_store_or_write(handle, ptr, len);
if (ret < 0) {
voprintf_error(" USB Dump: store and write failed.\n");
return -1;
} else if (ret == 0) {
break;
} else {
moves = len - ret;
ptr += moves;
len = ret;
}
}
return size;
}
int kdump_usb_output(const struct mrdump_control_block *mrdump_cb,
const struct kzip_addlist *memlist)
{
int i;
struct kzip_addlist expdb_file[2];
char *expdb_filename;
unsigned int expdb_offset, expdb_filesize;
voprintf_info("Output by USB\n");
struct mrdump_usb_handle *handle = memalign(64, sizeof(struct mrdump_usb_handle));
if (handle == NULL) {
voprintf_error("No enough memory.");
return -1;
}
memset(handle, 0, sizeof(struct mrdump_usb_handle));
mdelay(100);
bool ok = true;
mtk_wdt_restart();
struct kzip_file *zf = kzip_open(handle, usb_write_cb);
if (zf != NULL) {
/* add SYS_COREDUMP */
if (!kzip_add_file(zf, memlist, "SYS_COREDUMP"))
ok = false;
/* add file on expdb */
for (i = 0; i < IPANIC_NR_SECTIONS; i++) {
if (kedump_get_data_info(i, &expdb_filename, &expdb_offset, &expdb_filesize) == 0) {
expdb_file[0].address = expdb_offset;
expdb_file[0].size = expdb_filesize;
expdb_file[0].type = EXPDB_FILE;
expdb_file[1].address = 0;
expdb_file[1].size = 0;
expdb_file[1].type = MEM_NO_MAP;
if (!kzip_add_file(zf, expdb_file, expdb_filename))
ok = false;
}
}
/* close zipfile */
kzip_close(zf);
usb_write_cb(handle, NULL, 0); /* really write the last part */
zf = NULL;
} else {
ok = false;
}
free(handle);
mtk_wdt_restart();
if (ok) {
mrdump_status_ok("OUTPUT:%s\nMODE:%s\n", "USB DUMP",
mrdump_mode2string(mrdump_cb->crash_record.reboot_mode));
}
return ok ? 0 : -1;
}
#endif