Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
422 lines
9.0 KiB
C
422 lines
9.0 KiB
C
/*
|
|
* Copyright (C) 2017 MediaTek Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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 <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/sysfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/compiler.h>
|
|
|
|
#include <linux/udp.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/input.h>
|
|
#include "mix_event.h"
|
|
|
|
static struct class *s_ccci_ge_class;
|
|
static dev_t s_ge_status_dev;
|
|
struct cdev s_ge_char_dev;
|
|
|
|
#define SKB_TIME_BUF_LEN 100
|
|
|
|
///struct skb_time_data {
|
|
// __u16 port;
|
|
// struct timeval time;
|
|
//};
|
|
|
|
struct user_skb_times {
|
|
int count;
|
|
int beg;
|
|
int end;
|
|
//struct skb_time_data times[SKB_TIME_BUF_LEN];
|
|
struct input_event times[SKB_TIME_BUF_LEN];
|
|
wait_queue_head_t wait;
|
|
int pending_event_cnt;
|
|
|
|
int user_id;
|
|
int time_interval;
|
|
int once_read_count;
|
|
|
|
__be16 dest_port;
|
|
u64 last_skb_time;
|
|
|
|
} skb_times_ring;
|
|
|
|
static spinlock_t s_event_update_lock;
|
|
|
|
static void ccci_util_skbtime_initdata(void)
|
|
{
|
|
unsigned long flag;
|
|
|
|
spin_lock_irqsave(&s_event_update_lock, flag);
|
|
|
|
skb_times_ring.count = 0;
|
|
skb_times_ring.beg = 0;
|
|
skb_times_ring.end = 0;
|
|
|
|
skb_times_ring.last_skb_time = 0;
|
|
|
|
skb_times_ring.user_id = -1;
|
|
skb_times_ring.time_interval = -1; //5 * 1000000; //5ms
|
|
skb_times_ring.once_read_count = -1; //all skb times
|
|
|
|
skb_times_ring.pending_event_cnt = 0;
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
|
|
}
|
|
|
|
static int ccci_util_skbtime_hasdata(void)
|
|
{
|
|
unsigned long flag;
|
|
int hasdata;
|
|
|
|
spin_lock_irqsave(&s_event_update_lock, flag);
|
|
|
|
hasdata = skb_times_ring.count > 0;
|
|
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
|
|
return hasdata;
|
|
}
|
|
|
|
static int ccci_util_skbtime_getdata(struct input_event *data)
|
|
{
|
|
unsigned long flag;
|
|
|
|
spin_lock_irqsave(&s_event_update_lock, flag);
|
|
|
|
if (skb_times_ring.count > 0) {
|
|
if (skb_times_ring.beg < 0
|
|
|| skb_times_ring.beg >= SKB_TIME_BUF_LEN) {
|
|
pr_notice("%s:invalid index = %d\n",
|
|
__func__, skb_times_ring.beg);
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
return 0;
|
|
}
|
|
*data = skb_times_ring.times[skb_times_ring.beg];
|
|
|
|
if (skb_times_ring.beg == SKB_TIME_BUF_LEN - 1)
|
|
skb_times_ring.beg = 0;
|
|
else
|
|
skb_times_ring.beg++;
|
|
|
|
skb_times_ring.count--;
|
|
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
return 1;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
return 0;
|
|
}
|
|
|
|
static void ccci_util_skbtime_adddata(__be16 ipid, int skb_len, __u16 port)
|
|
{
|
|
unsigned long flag;
|
|
int index;
|
|
|
|
spin_lock_irqsave(&s_event_update_lock, flag);
|
|
|
|
index = skb_times_ring.end;
|
|
if (index < 0 || index >= SKB_TIME_BUF_LEN) {
|
|
pr_notice("%s:invalid index = %d\n", __func__, index);
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
return;
|
|
}
|
|
if (skb_times_ring.count == SKB_TIME_BUF_LEN) { // buff is full
|
|
if (skb_times_ring.beg == SKB_TIME_BUF_LEN - 1) {
|
|
skb_times_ring.beg = 0;
|
|
skb_times_ring.end = 0;
|
|
} else {
|
|
skb_times_ring.beg++;
|
|
skb_times_ring.end++;
|
|
}
|
|
} else { // buff no full
|
|
if (skb_times_ring.end == SKB_TIME_BUF_LEN - 1) // end is tail
|
|
skb_times_ring.end = 0;
|
|
else
|
|
skb_times_ring.end++;
|
|
|
|
skb_times_ring.count++;
|
|
}
|
|
//timeval_to_ktime
|
|
do_gettimeofday(&(skb_times_ring.times[index].time));
|
|
//skb_times_ring.times[index].time = ktime_to_timeval(*ktm);
|
|
//skb_times_ring.times[index].time.tv_sec = ktm.
|
|
skb_times_ring.times[index].type = port;
|
|
skb_times_ring.times[index].code = ipid;
|
|
skb_times_ring.times[index].value = skb_len;
|
|
|
|
spin_unlock_irqrestore(&s_event_update_lock, flag);
|
|
}
|
|
|
|
void inject_mix_event(struct sk_buff *skb, struct net_device *dev,
|
|
struct iphdr *iph)
|
|
{
|
|
if (iph->protocol != IPPROTO_UDP)
|
|
return;
|
|
|
|
//if (skb->sk->sk_state != TCP_ESTABLISHED)
|
|
// return;
|
|
|
|
if ((skb_times_ring.user_id == 999999 ||
|
|
skb_times_ring.user_id == skb->sk->sk_uid.val) &&
|
|
strncmp(dev->name, "ccmni", 5) == 0) {
|
|
|
|
if (skb_times_ring.time_interval > 0 &&
|
|
(sched_clock() - skb_times_ring.last_skb_time)
|
|
< skb_times_ring.time_interval) {
|
|
return;
|
|
}
|
|
|
|
skb_times_ring.last_skb_time = sched_clock();
|
|
|
|
//ktm = ktime_get();
|
|
ccci_util_skbtime_adddata(ntohs(iph->id),
|
|
ntohs(iph->tot_len), ntohs(udp_hdr(skb)->dest));
|
|
|
|
skb_times_ring.pending_event_cnt = 1;
|
|
wake_up_interruptible(&skb_times_ring.wait);
|
|
}
|
|
}
|
|
|
|
static int mix_open(struct inode *inode, struct file *filp)
|
|
{
|
|
filp->private_data = NULL;
|
|
nonseekable_open(inode, filp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mix_release(struct inode *inode, struct file *filp)
|
|
{
|
|
ccci_util_skbtime_initdata();
|
|
wake_up_interruptible(&skb_times_ring.wait);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ccci_util_analyze_parameter(char *temp_buf, int size)
|
|
{
|
|
char *psub, *pname, *pvalue;
|
|
long user_id = -1;
|
|
long time_interval = -1;
|
|
long read_count = -1;
|
|
|
|
pname = temp_buf;
|
|
while (true) {
|
|
psub = strchr(pname, '|');
|
|
|
|
if (psub) {
|
|
*psub = '\0';
|
|
psub += 1;
|
|
}
|
|
|
|
pvalue = strchr(pname, '=');
|
|
if (pvalue) {
|
|
*pvalue = '\0';
|
|
pvalue += 1;
|
|
}
|
|
|
|
if (strstr(pname, "user_id")) {
|
|
if (pvalue && *pvalue)
|
|
if (kstrtol(pvalue, 10, &user_id))
|
|
return 0;
|
|
} else if (strstr(pname, "time_interval")) {
|
|
if (pvalue && *pvalue)
|
|
if (kstrtol(pvalue, 10, &time_interval))
|
|
return 0;
|
|
} else if (strstr(pname, "read_count")) {
|
|
if (pvalue && *pvalue)
|
|
if (kstrtol(pvalue, 10, &read_count))
|
|
return 0;
|
|
}
|
|
|
|
if (!psub)
|
|
break;
|
|
|
|
pname = psub;
|
|
}
|
|
|
|
skb_times_ring.user_id = user_id;
|
|
//ns * 1000000 = ms
|
|
skb_times_ring.time_interval = time_interval * 1000000;
|
|
skb_times_ring.once_read_count = read_count;
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t mix_write(struct file *filp,
|
|
const char __user *buf, size_t size, loff_t *ppos)
|
|
{
|
|
char temp_buf[500];
|
|
|
|
if (size >= 500)
|
|
return -ENOMEM;
|
|
|
|
if (copy_from_user(temp_buf, buf, size))
|
|
return -EFAULT;
|
|
temp_buf[size] = '\0';
|
|
|
|
return ccci_util_analyze_parameter(temp_buf, size);
|
|
}
|
|
|
|
static int copy_skbtime_to_user(char __user *buf, int buf_len)
|
|
{
|
|
int offset = 0;
|
|
struct input_event data;
|
|
int readcount = 0;
|
|
|
|
|
|
while (buf_len >= sizeof(struct input_event)) {
|
|
if (!ccci_util_skbtime_getdata(&data))
|
|
break;
|
|
|
|
if (copy_to_user(buf + offset, &data,
|
|
sizeof(struct input_event))) {
|
|
offset = -EFAULT;
|
|
break; //return -EFAULT;
|
|
}
|
|
|
|
buf_len -= sizeof(struct input_event);
|
|
offset += sizeof(struct input_event);
|
|
|
|
if (skb_times_ring.once_read_count > 0) {
|
|
readcount++;
|
|
if (readcount >= skb_times_ring.once_read_count)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static ssize_t mix_read(struct file *filp,
|
|
char __user *buf, size_t size, loff_t *ppos)
|
|
{
|
|
int ret;
|
|
|
|
/* For Non-block read */
|
|
if (filp->f_flags & O_NONBLOCK) {
|
|
if (ccci_util_skbtime_hasdata() == 0) {
|
|
return -EAGAIN;
|
|
|
|
} else {
|
|
ret = copy_skbtime_to_user(buf, (int)size);
|
|
skb_times_ring.pending_event_cnt =
|
|
ccci_util_skbtime_hasdata();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* For block read */
|
|
while (1) {
|
|
if (ccci_util_skbtime_hasdata()) {
|
|
ret = copy_skbtime_to_user(buf, (int) size);
|
|
skb_times_ring.pending_event_cnt =
|
|
ccci_util_skbtime_hasdata();
|
|
return ret;
|
|
}
|
|
|
|
//skb_times_ring.pending_event_cnt > 0
|
|
//is true:exit, false:suspend
|
|
ret = wait_event_interruptible(skb_times_ring.wait,
|
|
skb_times_ring.pending_event_cnt > 0);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int mix_poll(struct file *filp,
|
|
struct poll_table_struct *wait)
|
|
{
|
|
unsigned int mask = 0;
|
|
|
|
poll_wait(filp, &skb_times_ring.wait, wait);
|
|
|
|
if (skb_times_ring.pending_event_cnt > 0)
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
return mask;
|
|
}
|
|
|
|
static const struct file_operations mix_event_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = mix_open,
|
|
.read = mix_read,
|
|
.write = mix_write,
|
|
.unlocked_ioctl = NULL,
|
|
.poll = mix_poll,
|
|
.release = mix_release,
|
|
};
|
|
|
|
int mix_event_init(void)
|
|
{
|
|
int ret;
|
|
|
|
spin_lock_init(&s_event_update_lock);
|
|
init_waitqueue_head(&skb_times_ring.wait);
|
|
ccci_util_skbtime_initdata();
|
|
|
|
ret = alloc_chrdev_region(&s_ge_status_dev, 0, 1, "mix_event");
|
|
if (ret) {
|
|
pr_notice("[mixdev] alloc chrdev fail (%d)\n", ret);
|
|
goto _exit_1;
|
|
}
|
|
|
|
cdev_init(&s_ge_char_dev, &mix_event_fops);
|
|
s_ge_char_dev.owner = THIS_MODULE;
|
|
|
|
ret = cdev_add(&s_ge_char_dev, s_ge_status_dev, 1);
|
|
if (ret) {
|
|
pr_notice("[mixdev] cdev_add failed\n");
|
|
goto _exit_2;
|
|
}
|
|
|
|
/* create a device node in directory /dev/ */
|
|
//dev_n = MKDEV(MAJOR(s_ge_status_dev), 0);
|
|
s_ccci_ge_class = class_create(THIS_MODULE, "mix_event");
|
|
device_create(s_ccci_ge_class, NULL,
|
|
s_ge_status_dev, NULL, "mix_event");
|
|
|
|
return 0;
|
|
|
|
_exit_2:
|
|
cdev_del(&s_ge_char_dev);
|
|
|
|
_exit_1:
|
|
unregister_chrdev_region(s_ge_status_dev, 1);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int __init __mix_event_init(void)
|
|
{
|
|
mix_event_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall(__mix_event_init);
|
|
MODULE_DESCRIPTION("MTK MIX EVENT Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|