323 lines
8.2 KiB
C
323 lines
8.2 KiB
C
/*
|
|
* xr806/debug.c
|
|
*
|
|
* Copyright (c) 2022
|
|
* Allwinner Technology Co., Ltd. <www.allwinner.com>
|
|
* laumy <liumingyuan@allwinner.com>
|
|
*
|
|
* Debug info implementation for Xr806 drivers
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
#include <linux/debugfs.h>
|
|
#include "xr_version.h"
|
|
#include "os_intf.h"
|
|
#include "xradio.h"
|
|
#include "debug.h"
|
|
|
|
#define XRADIO_DBG_DEFAULT (XRADIO_DBG_ALWY | XRADIO_DBG_ERROR)// | XRADIO_DBG_WARN) //|XRADIO_DBG_MSG)
|
|
|
|
u8 dbg_common = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_cfg = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_iface = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_hwio = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_plat = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_queue = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_spi = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_txrx = XRADIO_DBG_DEFAULT;
|
|
u8 dbg_cmd = XRADIO_DBG_DEFAULT;
|
|
|
|
/*for ip data*/
|
|
#define PF_TCP 0x0010
|
|
#define PF_UDP 0x0020
|
|
#define PF_DHCP 0x0040
|
|
#define PF_ICMP 0x0080
|
|
|
|
#define PF_IPADDR 0x2000 /*ip address of ip packets.*/
|
|
#define PF_UNKNWN 0x4000 /*print frames of unknown flag.*/
|
|
#define PF_RX 0x8000 /*0:TX, 1:RX. So, need to add PF_RX in Rx path.*/
|
|
|
|
#ifndef IPPROTO_IP
|
|
#define IPPROTO_IP 0
|
|
#endif
|
|
|
|
#ifndef IPPROTO_ICMP
|
|
#define IPPROTO_ICMP 1
|
|
#endif
|
|
|
|
#ifndef IPPROTO_TCP
|
|
#define IPPROTO_TCP 6
|
|
#endif
|
|
|
|
#ifndef IPPROTO_UDP
|
|
#define IPPROTO_UDP 17
|
|
#endif
|
|
|
|
#define ETH_P_IP 0x0800
|
|
#define ETH_P_ARP 0x0806
|
|
#define ETH_P_IPV6 0x86DD
|
|
|
|
#define PT_MSG_PUT(f, ...) \
|
|
do { \
|
|
if (flags & (f)) \
|
|
proto_msg += sprintf(proto_msg, __VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define IS_PROTO_PRINT (proto_msg != (char *)&protobuf[0])
|
|
|
|
#define LLC_LEN 14
|
|
#define LLC_TYPE_OFF 12 /*Ether type offset*/
|
|
#define IP_PROTO_OFF 9 /*protocol offset*/
|
|
#define IP_S_ADD_OFF 12
|
|
#define IP_D_ADD_OFF 16
|
|
#define UDP_LEN 8
|
|
/*DHCP*/
|
|
#define DHCP_BOOTP_C 68
|
|
#define DHCP_BOOTP_S 67
|
|
#define UDP_BOOTP_LEN 236 /*exclude "Options:64"*/
|
|
#define BOOTP_OPS_LEN 64
|
|
#define DHCP_MAGIC 0x63825363
|
|
#define DHCP_DISCOVER 0x01
|
|
#define DHCP_OFFER 0x02
|
|
#define DHCP_REQUEST 0x03
|
|
#define DHCP_DECLINE 0x04
|
|
#define DHCP_ACK 0x05
|
|
#define DHCP_NACK 0x06
|
|
#define DHCP_RELEASE 0x07
|
|
/*ARP*/
|
|
#define ARP_REQUEST 0x0001
|
|
#define ARP_RESPONSE 0x0002
|
|
#define ARP_TYPE_OFFSET 6
|
|
|
|
/*IP/IPV6/ARP layer...*/
|
|
static inline bool is_ip(u8 *eth_data)
|
|
{
|
|
/* 0x0800 */
|
|
return (bool)(*(u16 *)(eth_data + LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IP));
|
|
}
|
|
|
|
static inline bool is_ipv6(u8 *eth_data)
|
|
{
|
|
/* 0x08dd */
|
|
return (bool)(*(u16 *)(eth_data + LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IPV6));
|
|
}
|
|
|
|
static inline bool is_arp(u8 *eth_data)
|
|
{
|
|
/* 0x0806 */
|
|
return (bool)(*(u16 *)(eth_data + LLC_TYPE_OFF) == cpu_to_be16(ETH_P_ARP));
|
|
}
|
|
|
|
static inline bool is_tcp(u8 *eth_data)
|
|
{
|
|
return (bool)(eth_data[LLC_LEN + IP_PROTO_OFF] == IPPROTO_TCP);
|
|
}
|
|
|
|
static inline bool is_udp(u8 *eth_data)
|
|
{
|
|
return (bool)(eth_data[LLC_LEN + IP_PROTO_OFF] == IPPROTO_UDP);
|
|
}
|
|
|
|
static inline bool is_icmp(u8 *eth_data)
|
|
{
|
|
return (bool)(eth_data[LLC_LEN + IP_PROTO_OFF] == IPPROTO_ICMP);
|
|
}
|
|
|
|
static inline bool is_dhcp(u8 *eth_data)
|
|
{
|
|
u8 *ip_hdr = eth_data + LLC_LEN;
|
|
|
|
if (!is_ip(eth_data))
|
|
return (bool)0;
|
|
if (ip_hdr[IP_PROTO_OFF] == IPPROTO_UDP) {
|
|
u8 *udp_hdr = ip_hdr + ((ip_hdr[0] & 0xf) << 2); /*ihl:words*/
|
|
/* DHCP client or DHCP server*/
|
|
return (bool)((((udp_hdr[0] << 8) | udp_hdr[1]) == DHCP_BOOTP_C) ||
|
|
(((udp_hdr[0] << 8) | udp_hdr[1]) == DHCP_BOOTP_S));
|
|
}
|
|
return (bool)0;
|
|
}
|
|
|
|
void xradio_parse_frame(u8 *eth_data, u8 tx, u32 flags)
|
|
{
|
|
char protobuf[512] = {0};
|
|
char *proto_msg = &protobuf[0];
|
|
|
|
if (is_ip(eth_data)) {
|
|
u8 *ip_hdr = eth_data + LLC_LEN;
|
|
u8 *ipaddr_s = ip_hdr + IP_S_ADD_OFF;
|
|
u8 *ipaddr_d = ip_hdr + IP_D_ADD_OFF;
|
|
u8 *proto_hdr = ip_hdr + ((ip_hdr[0] & 0xf) << 2); /*ihl:words*/
|
|
|
|
if (is_tcp(eth_data)) {
|
|
PT_MSG_PUT(PF_TCP, "TCP%s%s, src=%d, dest=%d, seq=0x%08x, ack=0x%08x",
|
|
(proto_hdr[13] & 0x01) ? "(S)" : "",
|
|
(proto_hdr[13] & 0x02) ? "(F)" : "",
|
|
(proto_hdr[0] << 8) | proto_hdr[1],
|
|
(proto_hdr[2] << 8) | proto_hdr[3],
|
|
(proto_hdr[4] << 24) | (proto_hdr[5] << 16) |
|
|
(proto_hdr[6] << 8) | proto_hdr[7],
|
|
(proto_hdr[8] << 24) | (proto_hdr[9] << 16) |
|
|
(proto_hdr[10] << 8) | proto_hdr[11]);
|
|
|
|
} else if (is_udp(eth_data)) {
|
|
if (is_dhcp(eth_data)) {
|
|
u8 Options_len = BOOTP_OPS_LEN;
|
|
|
|
u32 dhcp_magic = cpu_to_be32(DHCP_MAGIC);
|
|
u8 *dhcphdr = proto_hdr + UDP_LEN + UDP_BOOTP_LEN;
|
|
|
|
while (Options_len) {
|
|
if (*(u32 *)dhcphdr == dhcp_magic)
|
|
break;
|
|
dhcphdr++;
|
|
Options_len--;
|
|
}
|
|
PT_MSG_PUT(PF_DHCP, "DHCP, Opt=%d, MsgType=%d", *(dhcphdr + 4),
|
|
*(dhcphdr + 6));
|
|
} else {
|
|
PT_MSG_PUT(PF_UDP, "UDP, source=%d, dest=%d",
|
|
(proto_hdr[0] << 8) | proto_hdr[1],
|
|
(proto_hdr[2] << 8) | proto_hdr[3]);
|
|
}
|
|
} else if (is_icmp(eth_data)) {
|
|
PT_MSG_PUT(PF_ICMP, "ICMP%s%s, Seq=%d", (proto_hdr[0] == 8) ? "(ping)" : "",
|
|
(proto_hdr[0] == 0) ? "(reply)" : "",
|
|
(proto_hdr[6] << 8) | proto_hdr[7]);
|
|
} else {
|
|
PT_MSG_PUT(PF_UNKNWN, "unknown IP type=%d", *(ip_hdr + IP_PROTO_OFF));
|
|
}
|
|
if (IS_PROTO_PRINT) {
|
|
PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(s)", ipaddr_s[0], ipaddr_s[1],
|
|
ipaddr_s[2], ipaddr_s[3]);
|
|
PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(d)", ipaddr_d[0], ipaddr_d[1],
|
|
ipaddr_d[2], ipaddr_d[3]);
|
|
xradio_printf("wlan0 %s: %s\n", tx ? "TX" : "RX", protobuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int xradio_version_show(struct seq_file *seq, void *v)
|
|
{
|
|
seq_printf(seq, "Driver Label:%s\n", XRADIO_VERSION);
|
|
return 0;
|
|
}
|
|
|
|
static int xradio_version_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, &xradio_version_show, inode->i_private);
|
|
}
|
|
|
|
static int xradio_generic_open(struct inode *inode, struct file *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations fops_version = {
|
|
.open = xradio_version_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
extern u16 txparse_flags;
|
|
extern u16 rxparse_flags;
|
|
|
|
static ssize_t xradio_parse_flags_set(struct file *file,
|
|
const char __user *user_buf,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
char buf[30] = { 0 };
|
|
char *start = &buf[0];
|
|
char *endptr = NULL;
|
|
|
|
count = (count > 29 ? 29 : count);
|
|
|
|
if (!count)
|
|
return -EINVAL;
|
|
if (copy_from_user(buf, user_buf, count))
|
|
return -EFAULT;
|
|
|
|
txparse_flags = simple_strtoul(buf, &endptr, 16);
|
|
start = endptr + 1;
|
|
if (start < buf + count)
|
|
rxparse_flags = simple_strtoul(start, &endptr, 16);
|
|
|
|
txparse_flags &= 0x7fff;
|
|
rxparse_flags &= 0x7fff;
|
|
|
|
xradio_dbg(XRADIO_DBG_ALWY, "txparse=0x%04x, rxparse=0x%04x\n",
|
|
txparse_flags, rxparse_flags);
|
|
return count;
|
|
}
|
|
|
|
static ssize_t xradio_parse_flags_get(struct file *file,
|
|
char __user *user_buf, size_t count, loff_t *ppos)
|
|
{
|
|
char buf[100];
|
|
size_t size = 0;
|
|
sprintf(buf, "txparse=0x%04x, rxparse=0x%04x\n",
|
|
txparse_flags, rxparse_flags);
|
|
|
|
size = strlen(buf);
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos, buf, size);
|
|
}
|
|
|
|
static const struct file_operations fops_parse_flags = {
|
|
.open = xradio_generic_open,
|
|
.write = xradio_parse_flags_set,
|
|
.read = xradio_parse_flags_get,
|
|
.llseek = default_llseek,
|
|
};
|
|
|
|
int xradio_debug_init_common(struct xradio_priv *priv)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
struct xradio_debug_common *d = NULL;
|
|
|
|
#define ERR_LINE do { goto err; } while (0)
|
|
|
|
d = xradio_k_zmalloc(sizeof(struct xradio_debug_common));
|
|
if (!d) {
|
|
xradio_dbg(XRADIO_DBG_ERROR, "malloc failed.\n");
|
|
return ret;
|
|
}
|
|
|
|
priv->debug = d;
|
|
|
|
d->debugfs_phy = debugfs_create_dir("xradio", NULL);
|
|
if (!d->debugfs_phy)
|
|
ERR_LINE;
|
|
|
|
if (!debugfs_create_file("version", S_IRUSR, d->debugfs_phy,
|
|
priv, &fops_version))
|
|
ERR_LINE;
|
|
|
|
if (!debugfs_create_file("parse_flags", S_IRUSR | S_IWUSR,
|
|
d->debugfs_phy, priv, &fops_parse_flags))
|
|
ERR_LINE;
|
|
|
|
return 0;
|
|
err:
|
|
debugfs_remove_recursive(d->debugfs_phy);
|
|
xradio_k_free(d);
|
|
return ret;
|
|
}
|
|
|
|
void xradio_debug_deinit_common(struct xradio_priv *priv)
|
|
{
|
|
struct xradio_debug_common *d = priv->debug;
|
|
|
|
if (d) {
|
|
priv->debug = NULL;
|
|
xradio_k_free(d);
|
|
}
|
|
}
|