186 lines
4.3 KiB
C
186 lines
4.3 KiB
C
|
/*
|
||
|
* Copyright (C) 2016 Samsung Electronics.
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "modem_prj.h"
|
||
|
#include "modem_utils.h"
|
||
|
#include "link_device_memory.h"
|
||
|
|
||
|
#ifdef GROUP_MEM_LINK_DEBUG
|
||
|
|
||
|
static int save_dump_file(struct link_device *ld, struct io_device *iod,
|
||
|
unsigned long arg, u8 __iomem *dump_base, size_t dump_size)
|
||
|
{
|
||
|
size_t copied = 0;
|
||
|
struct sk_buff *skb;
|
||
|
size_t alloc_size = 0xE00;
|
||
|
int ret;
|
||
|
|
||
|
if (dump_size == 0 || dump_base == NULL) {
|
||
|
mif_err("ERR! save_dump_file fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
ret = copy_to_user((void __user *)arg, &dump_size, sizeof(dump_size));
|
||
|
if (ret) {
|
||
|
mif_err("ERR! copy_from_user fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
while (copied < dump_size) {
|
||
|
if (dump_size - copied < alloc_size)
|
||
|
alloc_size = dump_size - copied;
|
||
|
|
||
|
skb = alloc_skb(alloc_size, GFP_ATOMIC);
|
||
|
if (!skb) {
|
||
|
skb_queue_purge(&iod->sk_rx_q);
|
||
|
mif_err("ERR! alloc_skb fail, purged skb_rx_q\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
memcpy(skb_put(skb, alloc_size), dump_base + copied,
|
||
|
alloc_size);
|
||
|
copied += alloc_size;
|
||
|
|
||
|
/* Record the IO device and the link device into the &skb->cb */
|
||
|
skbpriv(skb)->iod = iod;
|
||
|
skbpriv(skb)->ld = ld;
|
||
|
|
||
|
skbpriv(skb)->lnk_hdr = false;
|
||
|
skbpriv(skb)->sipc_ch = iod->id;
|
||
|
|
||
|
ret = iod->recv_skb_single(iod, ld, skb);
|
||
|
if (unlikely(ret < 0)) {
|
||
|
struct modem_ctl *mc = ld->mc;
|
||
|
|
||
|
mif_err_limited("%s: %s<-%s: %s->recv_skb fail (%d)\n",
|
||
|
ld->name, iod->name, mc->name, iod->name, ret);
|
||
|
dev_kfree_skb_any(skb);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mif_info("Complete! (%zu bytes)\n", copied);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int save_vss_dump(struct link_device *ld, struct io_device *iod,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
||
|
size_t vss_size = shm_get_vss_size();
|
||
|
|
||
|
if (vss_size == 0 || mld->vss_base == NULL) {
|
||
|
mif_err("ERR! save_vss_dump fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
return save_dump_file(ld, iod, arg, mld->vss_base, vss_size);
|
||
|
}
|
||
|
|
||
|
int save_acpm_dump(struct link_device *ld, struct io_device *iod,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
||
|
size_t acpm_size = shm_get_acpm_size();
|
||
|
|
||
|
if (acpm_size == 0 || mld->acpm_base == NULL) {
|
||
|
mif_err("ERR! save_acpm_dump fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
return save_dump_file(ld, iod, arg, mld->acpm_base, acpm_size);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_CP_RAM_LOGGING
|
||
|
int save_cplog_dump(struct link_device *ld, struct io_device *iod,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
void __iomem *cplog_base;
|
||
|
size_t cplog_size;
|
||
|
|
||
|
if (!shm_get_cplog_flag()) {
|
||
|
mif_err("cplog is not on. Skip cplog dump\n");
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
cplog_base = shm_get_cplog_region();
|
||
|
cplog_size = shm_get_cplog_size();
|
||
|
|
||
|
if (cplog_size == 0 || cplog_base == 0) {
|
||
|
mif_err("ERR! save_cplog_dump fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
return save_dump_file(ld, iod, arg, cplog_base, cplog_size);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int save_shmem_dump(struct link_device *ld, struct io_device *iod,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
struct mem_link_device *mld = to_mem_link_device(ld);
|
||
|
size_t shmem_size = mld->size;
|
||
|
|
||
|
if (shmem_size == 0 || mld->base == NULL) {
|
||
|
mif_err("ERR! save_shmem_dump fail!\n");
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
return save_dump_file(ld, iod, arg, mld->base, shmem_size);
|
||
|
}
|
||
|
|
||
|
void save_mem_dump(struct mem_link_device *mld)
|
||
|
{
|
||
|
#ifdef DEBUG_MODEM_IF
|
||
|
struct link_device *ld = &mld->link_dev;
|
||
|
char *path = mld->dump_path;
|
||
|
struct file *fp;
|
||
|
struct utc_time t;
|
||
|
|
||
|
get_utc_time(&t);
|
||
|
snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump",
|
||
|
MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min,
|
||
|
t.sec);
|
||
|
|
||
|
fp = mif_open_file(path);
|
||
|
if (!fp) {
|
||
|
mif_err("%s: ERR! %s open fail\n", ld->name, path);
|
||
|
return;
|
||
|
}
|
||
|
mif_err("%s: %s opened\n", ld->name, path);
|
||
|
|
||
|
mif_save_file(fp, mld->base, mld->size);
|
||
|
|
||
|
mif_close_file(fp);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void mem_dump_work(struct work_struct *ws)
|
||
|
{
|
||
|
#ifdef DEBUG_MODEM_IF
|
||
|
struct mem_link_device *mld;
|
||
|
|
||
|
mld = container_of(ws, struct mem_link_device, dump_work);
|
||
|
if (!mld) {
|
||
|
mif_err("ERR! no mld\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
save_mem_dump(mld);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#endif
|