forked from rrcarlosr/Jetpack
633 lines
16 KiB
C
633 lines
16 KiB
C
/*
|
|
* drivers/net/wireless/bcmdhd/dhd_custom_sysfs_tegra_tcpdump.c
|
|
*
|
|
* NVIDIA Tegra Sysfs for BCMDHD driver
|
|
*
|
|
* Copyright (C) 2014-2016 NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* 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 "dhd_custom_sysfs_tegra.h"
|
|
#include <linux/jiffies.h>
|
|
#include <linux/spinlock_types.h>
|
|
|
|
#ifndef TCPDUMP_NETIF_MAXSIZ
|
|
#define TCPDUMP_NETIF_MAXSIZ 16
|
|
#endif
|
|
|
|
#ifndef TCPDUMP_DATA_MAXSIZ
|
|
#define TCPDUMP_DATA_MAXSIZ 64
|
|
#endif
|
|
|
|
#ifndef PWRDUMP_DATA_MAXSIZ
|
|
#define PWRDUMP_DATA_MAXSIZ 128
|
|
#endif
|
|
|
|
#ifndef STATDUMP_DATA_MAXSIZ
|
|
#define STATDUMP_DATA_MAXSIZ 2560
|
|
#endif
|
|
|
|
#if defined(CONFIG_MACH_ARDBEG)
|
|
#define TCPDUMP_MAXSIZ (1 * 1024 * 1024)
|
|
#elif defined(CONFIG_MACH_T132REF)
|
|
#define TCPDUMP_MAXSIZ (3 * 1024 * 1024)
|
|
#endif
|
|
|
|
#ifndef TCPDUMP_MAXSIZ
|
|
#define TCPDUMP_MAXSIZ (3 * 1024 * 1024)
|
|
#endif
|
|
|
|
/* delay between rx packet and running statistics work function
|
|
* - ensures that statistics updated more frequently if rx is active
|
|
*/
|
|
|
|
#ifndef TCPDUMP_RX_STAT_DELAY
|
|
#define TCPDUMP_RX_STAT_DELAY 5 /* ms */
|
|
#endif
|
|
|
|
/* packet logs */
|
|
|
|
typedef struct {
|
|
unsigned long serial_no;
|
|
unsigned long time;
|
|
char tag;
|
|
char netif[TCPDUMP_NETIF_MAXSIZ];
|
|
const char *func;
|
|
int line;
|
|
unsigned int data_nonpaged_len;
|
|
unsigned int data_paged_len;
|
|
} multidump_pkt_t;
|
|
|
|
typedef struct {
|
|
int head;
|
|
int tail;
|
|
unsigned long serial_no;
|
|
multidump_pkt_t *pkt;
|
|
int maxpkt;
|
|
} multidump_pkt_log_t;
|
|
|
|
static multidump_pkt_t tcpdump_pkt
|
|
[(TCPDUMP_MAXSIZ * 8 / 10) /
|
|
(sizeof(multidump_pkt_t) + TCPDUMP_DATA_MAXSIZ)];
|
|
|
|
static multidump_pkt_t pwrdump_pkt
|
|
[(TCPDUMP_MAXSIZ * 1 / 10) /
|
|
(sizeof(multidump_pkt_t) + PWRDUMP_DATA_MAXSIZ)];
|
|
|
|
static multidump_pkt_t statdump_pkt
|
|
[(TCPDUMP_MAXSIZ * 1 / 10) /
|
|
(sizeof(multidump_pkt_t) + STATDUMP_DATA_MAXSIZ)];
|
|
|
|
static unsigned char tcpdump_pkt_data
|
|
[sizeof(tcpdump_pkt) / sizeof(tcpdump_pkt[0])]
|
|
[TCPDUMP_DATA_MAXSIZ];
|
|
|
|
static unsigned char pwrdump_pkt_data
|
|
[sizeof(pwrdump_pkt) / sizeof(pwrdump_pkt[0])]
|
|
[PWRDUMP_DATA_MAXSIZ];
|
|
|
|
static unsigned char statdump_pkt_data
|
|
[sizeof(statdump_pkt) / sizeof(statdump_pkt[0])]
|
|
[STATDUMP_DATA_MAXSIZ];
|
|
|
|
static multidump_pkt_log_t multidump_pkt_log[3] = {
|
|
/* first log is for tcpdump */
|
|
{
|
|
.pkt = tcpdump_pkt,
|
|
.maxpkt = sizeof(tcpdump_pkt) / sizeof(tcpdump_pkt[0]),
|
|
},
|
|
/* second log is for pwrdump */
|
|
{
|
|
.pkt = pwrdump_pkt,
|
|
.maxpkt = sizeof(pwrdump_pkt) / sizeof(pwrdump_pkt[0]),
|
|
},
|
|
/* third log is for statdump */
|
|
{
|
|
.pkt = statdump_pkt,
|
|
.maxpkt = sizeof(statdump_pkt) / sizeof(statdump_pkt[0]),
|
|
},
|
|
};
|
|
|
|
/* macros for accessing tcpdump log */
|
|
|
|
#define tcpdump_head (multidump_pkt_log[0].head)
|
|
#define tcpdump_tail (multidump_pkt_log[0].tail)
|
|
#define tcpdump_serial_no (multidump_pkt_log[0].serial_no)
|
|
#define tcpdump_pkt(i) (multidump_pkt_log[0].pkt + i)
|
|
#define tcpdump_maxpkt (multidump_pkt_log[0].maxpkt)
|
|
|
|
/* macros for accessing pwrdump log */
|
|
|
|
#define pwrdump_head (multidump_pkt_log[1].head)
|
|
#define pwrdump_tail (multidump_pkt_log[1].tail)
|
|
#define pwrdump_serial_no (multidump_pkt_log[1].serial_no)
|
|
#define pwrdump_pkt(i) (multidump_pkt_log[1].pkt + i)
|
|
#define pwrdump_maxpkt (multidump_pkt_log[1].maxpkt)
|
|
|
|
/* macros for accessing statdump log */
|
|
|
|
#define statdump_head (multidump_pkt_log[2].head)
|
|
#define statdump_tail (multidump_pkt_log[2].tail)
|
|
#define statdump_serial_no (multidump_pkt_log[2].serial_no)
|
|
#define statdump_pkt(i) (multidump_pkt_log[2].pkt + i)
|
|
#define statdump_maxpkt (multidump_pkt_log[2].maxpkt)
|
|
|
|
/* macros for accessing *dump log */
|
|
|
|
#define multidump_head (multidump_pkt_log[multi].head)
|
|
#define multidump_tail (multidump_pkt_log[multi].tail)
|
|
#define multidump_serial_no (multidump_pkt_log[multi].serial_no)
|
|
#define multidump_pkt(i) (multidump_pkt_log[multi].pkt + i)
|
|
#define multidump_maxpkt (multidump_pkt_log[multi].maxpkt)
|
|
/* If multi is not set default multi is 0 do replacing null with
|
|
&(tcpdump_pkt_data[i][0]) but not updating max length. it will
|
|
be 0 which means no memcpy will happen */
|
|
#define multidump_pkt_data(i) (\
|
|
(multi == 0)\
|
|
? &(tcpdump_pkt_data[i][0]) \
|
|
: (multi == 1)\
|
|
? &(pwrdump_pkt_data[i][0]) \
|
|
: (multi == 2)\
|
|
? &(statdump_pkt_data[i][0]) \
|
|
: &(tcpdump_pkt_data[i][0])\
|
|
)
|
|
#define multidump_pkt_data_max_len(i)\
|
|
(\
|
|
(multi == 0)\
|
|
? sizeof(tcpdump_pkt_data[i]) \
|
|
: (multi == 1)\
|
|
? sizeof(pwrdump_pkt_data[i]) \
|
|
: (multi == 2)\
|
|
? sizeof(statdump_pkt_data[i]) \
|
|
: 0\
|
|
)
|
|
|
|
/* */
|
|
|
|
static int multidump_get(void)
|
|
{
|
|
int selected_pkt_log;
|
|
int selected_pkt_head;
|
|
int multi;
|
|
|
|
selected_pkt_log = -1;
|
|
selected_pkt_head = -1;
|
|
for (multi = 0; multi < 3; multi++) {
|
|
if (multidump_maxpkt <= 0)
|
|
continue;
|
|
if (multidump_head == multidump_tail)
|
|
continue;
|
|
if ((selected_pkt_log == -1)
|
|
|| time_before(multidump_pkt(multidump_head)->time,
|
|
multidump_pkt_log[selected_pkt_log].pkt
|
|
[selected_pkt_head].time)) {
|
|
selected_pkt_log = multi;
|
|
selected_pkt_head = multidump_head;
|
|
}
|
|
}
|
|
|
|
return selected_pkt_log;
|
|
|
|
}
|
|
|
|
/* */
|
|
|
|
DEFINE_SPINLOCK(tcpdump_lock);
|
|
|
|
static DEFINE_SEMAPHORE(tcpdump_read_lock);
|
|
|
|
extern int lp0_logs_enable;
|
|
static int pkt_save;
|
|
static int pkt_rx_save = 1;
|
|
static int pkt_tx_save = 1;
|
|
static atomic_t insert_dummy_timestamp = ATOMIC_INIT(1);
|
|
static struct timespec time_stamp;
|
|
|
|
struct dummy_time {
|
|
unsigned long time_sec;
|
|
unsigned long jiffies;
|
|
unsigned long time_nsec;
|
|
} dt;
|
|
|
|
static void
|
|
tcpdump_set_maxpkt(int maxpkt)
|
|
{
|
|
unsigned long flags;
|
|
|
|
pr_info("%s: maxpkt %d\n", __func__, maxpkt);
|
|
|
|
/* set max packet */
|
|
spin_lock_irqsave(&tcpdump_lock, flags);
|
|
tcpdump_head = 0;
|
|
tcpdump_tail = 0;
|
|
tcpdump_maxpkt = maxpkt;
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
|
|
}
|
|
|
|
void
|
|
tcpdump_pkt_save(char tag, const char *netif, const char *func, int line,
|
|
const unsigned char *data,
|
|
unsigned int data_nonpaged_len,
|
|
unsigned int data_paged_len)
|
|
{
|
|
int multi;
|
|
multidump_pkt_t pkt;
|
|
unsigned int pkt_data_max_len;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
/* pick which log to save packet in - (tcp/pwr/stat/...)dump */
|
|
multi = 0;
|
|
if (tag == TCPDUMP_TAG_PWR)
|
|
multi = 1;
|
|
else if (tag == TCPDUMP_TAG_STAT)
|
|
multi = 2;
|
|
|
|
/* check if (tcp/pwr/stat/...)dump enabled */
|
|
if (multidump_maxpkt <= 0)
|
|
return;
|
|
|
|
/* check if (tcp/pwr/stat/...)dump packet save enable */
|
|
if ((multi == 0) && (pkt_save == 0))
|
|
return;
|
|
|
|
/* copy (tcp/pwr/stat/...)dump pkt */
|
|
pkt.serial_no = 0;
|
|
pkt.time = 0;
|
|
pkt.tag = tag;
|
|
strcpy(pkt.netif, netif);
|
|
pkt.func = func;
|
|
pkt.line = line;
|
|
pkt.data_nonpaged_len = data_nonpaged_len;
|
|
pkt.data_paged_len = data_paged_len;
|
|
|
|
/* save (tcp/pwr/stat/...)dump pkt */
|
|
spin_lock_irqsave(&tcpdump_lock, flags);
|
|
if (multidump_maxpkt <= 0) {
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
return;
|
|
} else {
|
|
i = multidump_tail;
|
|
multidump_tail = (multidump_tail + 1) % multidump_maxpkt;
|
|
if (multidump_tail == multidump_head)
|
|
multidump_head = (multidump_head + 1)
|
|
% multidump_maxpkt;
|
|
}
|
|
pkt.serial_no = tcpdump_serial_no++;
|
|
pkt.time = jiffies;
|
|
*(multidump_pkt(i)) = pkt;
|
|
pkt_data_max_len = multidump_pkt_data_max_len(i);
|
|
if (data_nonpaged_len > pkt_data_max_len)
|
|
memcpy(multidump_pkt_data(i), data, pkt_data_max_len);
|
|
else
|
|
memcpy(multidump_pkt_data(i), data, data_nonpaged_len);
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
|
|
/* TODO - analyze packet jitter / etc. */
|
|
|
|
}
|
|
|
|
void
|
|
tegra_sysfs_histogram_tcpdump_rx(struct sk_buff *skb,
|
|
const char *func, int line)
|
|
{
|
|
struct net_device *netdev = skb ? skb->dev : NULL;
|
|
char *netif = netdev ? netdev->name : "";
|
|
|
|
/* check if rx packet logging enabled */
|
|
if (skb->protocol == ETHER_TYPE_BRCM_REV && lp0_logs_enable == 0)
|
|
return;
|
|
if (!pkt_rx_save)
|
|
return;
|
|
pr_debug_ratelimited("%s: %s(%d): %s\n", __func__, func, line, netif);
|
|
|
|
/* save rx packet */
|
|
tcpdump_pkt_save(TCPDUMP_TAG_RX, netif, func, line,
|
|
skb->data, skb_headlen(skb), skb->data_len);
|
|
|
|
/* kick off a stat work so we can get counters report */
|
|
#if TCPDUMP_RX_STAT_DELAY > 0
|
|
tegra_sysfs_histogram_stat_work_run(TCPDUMP_RX_STAT_DELAY);
|
|
#endif
|
|
|
|
}
|
|
|
|
void
|
|
tegra_sysfs_histogram_tcpdump_tx(struct sk_buff *skb,
|
|
const char *func, int line)
|
|
{
|
|
struct net_device *netdev = skb ? skb->dev : NULL;
|
|
char *netif = netdev ? netdev->name : "";
|
|
|
|
/* check if tx packet logging enabled */
|
|
if (!pkt_tx_save)
|
|
return;
|
|
pr_debug_ratelimited("%s: %s(%d): %s\n", __func__, func, line, netif);
|
|
|
|
/* save tx packet */
|
|
tcpdump_pkt_save(TCPDUMP_TAG_TX, netif, func, line,
|
|
skb->data, skb_headlen(skb), skb->data_len);
|
|
}
|
|
|
|
void
|
|
tegra_sysfs_histogram_tcpdump_work_start(void)
|
|
{
|
|
// pr_info("%s\n", __func__);
|
|
|
|
/* placeholder for tcpdump work */
|
|
|
|
}
|
|
|
|
void
|
|
tegra_sysfs_histogram_tcpdump_work_stop(void)
|
|
{
|
|
// pr_info("%s\n", __func__);
|
|
|
|
/* placeholder for tcpdump work */
|
|
|
|
}
|
|
|
|
char *
|
|
multidump_format_pkt(int multi, char *buf, multidump_pkt_t *pkt,
|
|
unsigned char *pkt_data)
|
|
{
|
|
char *s = buf;
|
|
unsigned int pkt_data_max_len = multidump_pkt_data_max_len(0);
|
|
int m, n;
|
|
sprintf(s,
|
|
"[%08lx|%08lx] %c %s: %s(%d): %u+%u\n",
|
|
pkt->serial_no,
|
|
pkt->time,
|
|
pkt->tag,
|
|
pkt->netif,
|
|
pkt->func,
|
|
pkt->line,
|
|
pkt->data_nonpaged_len,
|
|
pkt->data_paged_len);
|
|
s += strlen(s);
|
|
for (m = 0;
|
|
(m < pkt_data_max_len) && (m < pkt->data_nonpaged_len);
|
|
m += n) {
|
|
for (n = 0; n < 16; n++) {
|
|
if (m + n >= pkt_data_max_len)
|
|
break;
|
|
if (m + n >= pkt->data_nonpaged_len)
|
|
break;
|
|
sprintf(s,
|
|
" %02x",
|
|
pkt_data[m + n]);
|
|
s += 3;
|
|
}
|
|
sprintf(s, "\n");
|
|
s++;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
extern const char dummy_inf[];
|
|
char *
|
|
tcpdump_insert_dummy_timestamp(char *buf)
|
|
{
|
|
multidump_pkt_t pkt;
|
|
dt.jiffies = jiffies;
|
|
getnstimeofday(&time_stamp);
|
|
dt.time_sec = time_stamp.tv_sec;
|
|
dt.time_nsec = time_stamp.tv_nsec;
|
|
|
|
/* Update the pkt structure for dummy timestamp.
|
|
* tcpdump_lock not needed as it will be already locked.
|
|
*/
|
|
pkt.time = jiffies;
|
|
pkt.tag = TCPDUMP_TAG_TIME;
|
|
memcpy(pkt.netif, dummy_inf, strlen(dummy_inf)+1); /* +1 for null */
|
|
pkt.func = "dummy_time";
|
|
pkt.line = 0;
|
|
pkt.data_nonpaged_len = sizeof(struct dummy_time);
|
|
pkt.data_paged_len = 0;
|
|
pkt.serial_no = tcpdump_serial_no++;
|
|
|
|
atomic_set(&insert_dummy_timestamp, 0);
|
|
return multidump_format_pkt(0 /* 0 = tcpdump pkt log */, buf, &pkt,
|
|
(unsigned char *) &dt);
|
|
}
|
|
|
|
#define SHOW_BUF_DATA_MAXSIZ /* must be max of *DUMP_DATA_MAXSIZ */\
|
|
STATDUMP_DATA_MAXSIZ
|
|
|
|
#if (SHOW_BUF_DATA_MAXSIZ < TCPDUMP_DATA_MAXSIZ) || \
|
|
(SHOW_BUF_DATA_MAXSIZ < PWRDUMP_DATA_MAXSIZ) || \
|
|
(SHOW_BUF_DATA_MAXSIZ < STATDUMP_DATA_MAXSIZ)
|
|
#error "SHOW_BUF_DATA_MAXSIZ too small"
|
|
#endif
|
|
|
|
#define SHOW_BUF_MAXSTRLEN\
|
|
(\
|
|
/* 1st line */ \
|
|
80 + \
|
|
/* number of rows (with 16 hexadecimal numbers per row) */ \
|
|
(((SHOW_BUF_DATA_MAXSIZ - 1) / 16) + 1) * \
|
|
/* number of characters per row (of 16 hexadecimal numbers) */ \
|
|
(3 * 16 + 1) \
|
|
)\
|
|
|
|
static char show_buf[SHOW_BUF_MAXSTRLEN];
|
|
static char *show_buf_head;
|
|
static char *show_buf_tail;
|
|
|
|
ssize_t
|
|
tegra_sysfs_histogram_tcpdump_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
int multi;
|
|
char *s;
|
|
unsigned int len;
|
|
multidump_pkt_t pkt;
|
|
unsigned long flags;
|
|
int i;
|
|
|
|
/* pr_info("%s\n", __func__); */
|
|
|
|
/* get/show (tcp/pwr/...)dump pkt(s) */
|
|
for (s = buf; (s - buf) < PAGE_SIZE; ) {
|
|
/* show tail of previous (tcp/pwr/...)dump pkt */
|
|
if ((show_buf_head != NULL)
|
|
&& (show_buf_tail != NULL)
|
|
&& (show_buf_tail != show_buf_head)) {
|
|
len = show_buf_tail - show_buf_head;
|
|
if (len > PAGE_SIZE - (s - buf))
|
|
len = PAGE_SIZE - (s - buf);
|
|
memcpy(s, show_buf_head, len);
|
|
s += len;
|
|
show_buf_head += len;
|
|
if (show_buf_head == show_buf_tail) {
|
|
show_buf_head = NULL;
|
|
show_buf_tail = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
/* get (tcp/pwr/...)dump pkt */
|
|
spin_lock_irqsave(&tcpdump_lock, flags);
|
|
multi = multidump_get();
|
|
if (multi < 0) {
|
|
atomic_set(&insert_dummy_timestamp, 1);
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
return s - buf;
|
|
}
|
|
if (multidump_maxpkt <= 0) {
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
return (s - buf);
|
|
} else if (multidump_head == multidump_tail) {
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
// pr_info("%s: no more tcpdump pkt(s)!\n", __func__);
|
|
return (s - buf);
|
|
} else {
|
|
if (atomic_read(&insert_dummy_timestamp))
|
|
s = tcpdump_insert_dummy_timestamp(s);
|
|
i = multidump_head++;
|
|
if (multidump_head >= multidump_maxpkt)
|
|
multidump_head = 0;
|
|
}
|
|
pkt = *(multidump_pkt(i));
|
|
spin_unlock_irqrestore(&tcpdump_lock, flags);
|
|
/* show (tcp/pwr/...)dump pkt */
|
|
show_buf_head = show_buf;
|
|
show_buf_tail = multidump_format_pkt(multi, show_buf_head,
|
|
&pkt, multidump_pkt_data(i));
|
|
}
|
|
return (s - buf);
|
|
}
|
|
|
|
ssize_t
|
|
tegra_sysfs_histogram_tcpdump_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int maxpkt;
|
|
int err;
|
|
|
|
// pr_info("%s\n", __func__);
|
|
|
|
if (strncmp(buf, "enable", 6) == 0) {
|
|
pr_info("%s: tcpdump enabled\n", __func__);
|
|
pkt_save = 1;
|
|
maxpkt = sizeof(tcpdump_pkt) / sizeof(tcpdump_pkt[0]);
|
|
} else if (strncmp(buf, "disable", 7) == 0) {
|
|
pr_info("%s: tcpdump disabled\n", __func__);
|
|
pkt_save = 0;
|
|
maxpkt = 0;
|
|
} else if (strncmp(buf, "stop", 4) == 0) {
|
|
pr_info("%s: tcpdump stopped\n", __func__);
|
|
pkt_save = 0;
|
|
return count;
|
|
} else if (strncmp(buf, "start", 5) == 0) {
|
|
pr_info("%s: tcpdump started\n", __func__);
|
|
pkt_save = 1;
|
|
return count;
|
|
} else if (strncmp(buf, "rxstop", 6) == 0) {
|
|
pr_info("%s: tcpdump rxstopped\n", __func__);
|
|
pkt_rx_save = 0;
|
|
return count;
|
|
} else if (strncmp(buf, "rxstart", 7) == 0) {
|
|
pr_info("%s: tcpdump rxstarted\n", __func__);
|
|
pkt_rx_save = 1;
|
|
return count;
|
|
} else if (strncmp(buf, "txstop", 6) == 0) {
|
|
pr_info("%s: tcpdump txstopped\n", __func__);
|
|
pkt_tx_save = 0;
|
|
return count;
|
|
} else if (strncmp(buf, "txstart", 7) == 0) {
|
|
pr_info("%s: tcpdump txstarted\n", __func__);
|
|
pkt_tx_save = 1;
|
|
return count;
|
|
} else if (strncmp(buf, "lp0_logs_start", 14) == 0) {
|
|
pr_info("%s: lp0 logs started\n", __func__);
|
|
lp0_logs_enable = 1;
|
|
return count;
|
|
} else if (strncmp(buf, "lp0_logs_stop", 13) == 0) {
|
|
pr_info("%s: lp0 logs stopped\n", __func__);
|
|
lp0_logs_enable = 0;
|
|
return count;
|
|
} else if (strncmp(buf, "test_pwrdump", 12) == 0) {
|
|
pr_info("%s: test pwrdump - add phony record\n", __func__);
|
|
tcpdump_pkt_save(TCPDUMP_TAG_PWR, "multi0", __func__, __LINE__,
|
|
(unsigned char *) buf + 12, count - 12, 0);
|
|
return count;
|
|
} else if (strncmp(buf, "test_statdump", 13) == 0) {
|
|
pr_info("%s: test statdump - add phony record\n", __func__);
|
|
tcpdump_pkt_save(TCPDUMP_TAG_STAT, "multi0", __func__, __LINE__,
|
|
(unsigned char *) buf + 13, count - 13, 0);
|
|
return count;
|
|
} else {
|
|
maxpkt = -1;
|
|
err = kstrtoint(buf, 0, &maxpkt);
|
|
if (maxpkt < 0) {
|
|
pr_err("%s: ignore invalid maxpkt %d\n",
|
|
__func__, maxpkt);
|
|
return count;
|
|
} else if (maxpkt > sizeof(tcpdump_pkt)
|
|
/ sizeof(tcpdump_pkt[0])) {
|
|
pr_info("%s: limit maxpkt from %d to %ld\n",
|
|
__func__,
|
|
maxpkt,
|
|
sizeof(tcpdump_pkt) / sizeof(tcpdump_pkt[0]));
|
|
maxpkt = sizeof(tcpdump_pkt) / sizeof(tcpdump_pkt[0]);
|
|
}
|
|
}
|
|
tcpdump_set_maxpkt(maxpkt);
|
|
|
|
return count;
|
|
}
|
|
|
|
ssize_t
|
|
tegra_debugfs_histogram_tcpdump_read(struct file *filp,
|
|
char __user *buff, size_t count, loff_t *offp)
|
|
{
|
|
static char buf[PAGE_SIZE];
|
|
struct device *dev = NULL;
|
|
struct device_attribute *attr = NULL;
|
|
ssize_t size, chunk;
|
|
|
|
// pr_info("%s\n", __func__);
|
|
|
|
/* lock read semaphore */
|
|
if (down_interruptible(&tcpdump_read_lock) < 0) {
|
|
pr_err("%s: cannot lock read semaphore\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
for (size = 0; size + PAGE_SIZE <= count; size += chunk) {
|
|
chunk = tegra_sysfs_histogram_tcpdump_show(dev, attr, buf);
|
|
if (chunk <= 0)
|
|
break;
|
|
if (copy_to_user(buff + size, buf, chunk) != 0) {
|
|
pr_err("%s: copy_to_user() failed!\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* unlock read semaphore */
|
|
up(&tcpdump_read_lock);
|
|
|
|
return size;
|
|
}
|
|
|
|
ssize_t
|
|
tegra_debugfs_histogram_tcpdump_write(struct file *filp,
|
|
const char __user *buff, size_t count, loff_t *offp)
|
|
{
|
|
// pr_info("%s\n", __func__);
|
|
return count;
|
|
}
|