| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/cdev.h> |
| #include <linux/semaphore.h> |
| #include <linux/timer.h> |
| |
| // #include <linux/fs.h> |
| #include <linux/ioport.h> |
| // #include <linux/serial_reg.h> |
| #include <linux/poll.h> |
| #include <linux/delay.h> |
| #include <linux/wait.h> |
| #include <linux/err.h> |
| #include <linux/interrupt.h> |
| #include <linux/sched.h> |
| #include <linux/types.h> |
| #include <linux/device.h> |
| #include <linux/miscdevice.h> |
| // #include <linux/spinlock.h> |
| #include <linux/list.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| // #include <linux/kthread.h> |
| #include <asm/io.h> |
| |
| #include <linux/vmalloc.h> |
| #include <linux/soc/zte/rpmsg.h> |
| // #include <linux/syscalls.h> |
| |
| // #include "debuginfo.h" |
| #include "pub_debug_info.h" |
| #include "ringbuf.h" |
| |
| |
| #if defined(_USE_ZXIC_DEBUG_INFO) && !defined(CONFIG_SYSTEM_RECOVERY) |
| /******************************************************************************* |
| * 宏定义 * |
| *******************************************************************************/ |
| #define DEBUG_INFO_SHARE_MEM_LEN (0x2000) |
| #define DEBUG_INFO_READABLE_LEN (0x1400) |
| #define DEBUG_INFO_MAX_DATA_LEN (128) |
| #define DEBUG_INFO_MAX_TOTAL_LEN (140) // 8 + 128 + 4 |
| #define DEBUG_INFO_READ_TIME_MSECS (10000) |
| |
| #define DEBUG_INFO_CHANNEL (9) |
| #define DEBUG_INFO_MSG_CAP_SIZE (2 * 1024) |
| |
| #define DEBUG_INFO_OK (0) |
| #define DEBUG_INFO_ERROR (-1) |
| |
| #define DEBUG_INFO_IOCTL_SET_DISABLE (0x1001) |
| |
| /******************************************************************************* |
| * 结构体定义 * |
| *******************************************************************************/ |
| typedef unsigned int UINT32; |
| typedef unsigned short UINT16; |
| typedef unsigned char UINT8; |
| |
| typedef struct |
| { |
| UINT16 module_id; // 模块id |
| UINT16 sub_len; // 用户数据长度 |
| UINT32 time; |
| char sub_data[]; // 用户数据 |
| } T_SHARED_MEM_DATA; |
| |
| typedef struct |
| { |
| UINT32 head; // 0x010a0a0a |
| UINT32 total_len; // 数据内容长度 |
| long long time; // time()函数获取 |
| } T_SAVE_FILE_DATA; |
| |
| /******************************************************************************* |
| * 全局变量 * |
| *******************************************************************************/ |
| volatile T_RINGBUFFER *g_debug_info_buf = NULL; |
| static struct semaphore debug_sem; |
| static DEFINE_RAW_SPINLOCK(debugWr_lock); |
| static int g_init_flag = 0; |
| |
| /******************************************************************************* |
| * 内部函数定义 * |
| *******************************************************************************/ |
| static int sc_debug_info_read_to_user(char *buf, unsigned short count); |
| static int sc_debug_info_record_from_user(const char *info, unsigned short count); |
| static int sc_debug_info_write(UINT32 flag, const UINT8 *buf, UINT32 len); |
| static void sc_debug_info_from_ap(void *buf, unsigned int len); |
| |
| static void kernel_timer_timeout(struct timer_list *t); |
| static ssize_t debug_info_read(struct file *fp, char __user *buf, size_t count, loff_t *pos); |
| static ssize_t debug_info_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos); |
| static int debug_info_open(struct inode *ip, struct file *fp); |
| static long debug_info_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); |
| static int debug_info_release(struct inode *ip, struct file *fp); |
| |
| //初始化timer |
| static DEFINE_TIMER(timer, kernel_timer_timeout); |
| |
| static const struct file_operations debug_info_fops = { |
| .owner = THIS_MODULE, |
| .read = debug_info_read, |
| .write = debug_info_write, |
| .open = debug_info_open, |
| .unlocked_ioctl = debug_info_ioctl, |
| .release = debug_info_release, |
| }; |
| |
| static struct miscdevice debug_info_device = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "debug_info", |
| .fops = &debug_info_fops, |
| }; |
| |
| static void kernel_timer_timeout(struct timer_list *t) |
| { |
| if (debug_sem.count == 0) |
| { |
| up(&debug_sem); |
| } |
| /* 因为内核定时器是一个单次的定时器,所以如果想要多次重复定时需要在定时器绑定的函数结尾重新装载时间,并启动定时 */ |
| /* Kernel Timer restart */ |
| mod_timer(&timer, jiffies + msecs_to_jiffies(DEBUG_INFO_READ_TIME_MSECS)); |
| } |
| |
| static ssize_t debug_info_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) |
| { |
| int ret; |
| int rd_len; |
| |
| ret = down_interruptible(&debug_sem); |
| if(ret < 0) |
| { |
| return ret; |
| } |
| else |
| { |
| rd_len = sc_debug_info_read_to_user(buf, count); |
| } |
| |
| return rd_len; |
| } |
| |
| static ssize_t debug_info_write(struct file *fp, const char __user *buf, size_t count, loff_t *pos) |
| { |
| int wr_len = sc_debug_info_record_from_user(buf, count); |
| |
| return wr_len; |
| } |
| |
| static int debug_info_open(struct inode *ip, struct file *fp) |
| { |
| return 0; |
| } |
| |
| static long debug_info_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) |
| { |
| switch(cmd) |
| { |
| case DEBUG_INFO_IOCTL_SET_DISABLE: |
| *(volatile UINT32 *)ZCAT_DEBUG_INFO_DISABLE = arg; |
| break; |
| |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static int debug_info_release(struct inode *ip, struct file *fp) |
| { |
| return 0; |
| } |
| |
| static void sc_debug_info_from_ap(void *buf, unsigned int len) |
| { |
| T_SHARED_MEM_DATA *debug_msg = (T_SHARED_MEM_DATA *)buf; |
| debug_msg->time = jiffies; |
| |
| sc_debug_info_write(ZCAT_MEM_TYPE_KERNEL, buf, len); |
| } |
| |
| static int __init debug_info_init(void) |
| { |
| int ret = misc_register(&debug_info_device); |
| if (ret) |
| { |
| printk("debug_info_device init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| void *mem; |
| mem = vmalloc(DEBUG_INFO_SHARE_MEM_LEN); |
| if (!mem) |
| { |
| printk("vmalloc failed.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| g_debug_info_buf = CreateRingBuffer((UINT8 *)mem, DEBUG_INFO_SHARE_MEM_LEN); |
| if (g_debug_info_buf == NULL) |
| { |
| printk("CreateRingBuffer failed.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| #if 1 |
| ret = rpmsgCreateChannel( |
| CORE_PS0, |
| DEBUG_INFO_CHANNEL, |
| DEBUG_INFO_MSG_CAP_SIZE); |
| if (ret != DEBUG_INFO_OK) |
| { |
| printk("rpmsgCreateChannel failed, ret = %d\n", ret); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| ret = rpmsgRegCallBack( |
| CORE_PS0, |
| DEBUG_INFO_CHANNEL, |
| sc_debug_info_from_ap); |
| if (ret != DEBUG_INFO_OK) |
| { |
| printk("rpmsgRegCallBack failed,ret = %d\n", ret); |
| return DEBUG_INFO_ERROR; |
| } |
| #endif |
| sema_init(&debug_sem, 0); |
| /* 添加并启动定时器, 10ms */ |
| mod_timer(&timer, jiffies + 1); |
| |
| g_init_flag = 1; |
| |
| return 0; |
| } |
| |
| static void __exit debug_info_exit(void) |
| { |
| misc_deregister(&debug_info_device); |
| |
| del_timer(&timer); |
| } |
| |
| static int sc_debug_info_write(UINT32 flag, const UINT8 *buf, UINT32 len) |
| { |
| UINT32 writelen; |
| UINT32 used_space; |
| unsigned long flags; |
| |
| if (len == 0 || g_debug_info_buf == NULL) |
| { |
| printk("sc_debug_info_write:: (len == 0 || g_debug_info_buf == NULL).\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| raw_spin_lock_irqsave(&debugWr_lock, flags); |
| writelen = WriteRingBuffer(g_debug_info_buf, buf, len, flag); |
| raw_spin_unlock_irqrestore(&debugWr_lock, flags); |
| used_space = GetRingBufferSize(g_debug_info_buf); |
| if (used_space > DEBUG_INFO_READABLE_LEN) |
| { |
| if (debug_sem.count == 0) |
| { |
| up(&debug_sem); |
| } |
| } |
| |
| return writelen; |
| } |
| |
| static int sc_debug_info_read_to_user(char *buf, unsigned short count) |
| { |
| unsigned int bufSize_used = 0; |
| unsigned int readLen = 0; |
| unsigned int bufLen = 0; |
| T_SAVE_FILE_DATA fileDataHead; |
| |
| if (g_init_flag == 0) |
| { |
| printk("debug_info not init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| if (count == 0 || buf == NULL || g_debug_info_buf == NULL) |
| { |
| printk("sc_debug_info_read_to_user:: (count == 0 || buf == NULL || g_debug_info_buf == NULL).\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| bufSize_used = GetRingBufferSize(g_debug_info_buf); |
| if (bufSize_used == 0) |
| { |
| // printk("sc_debug_info_read_to_user:: ringBuf is empty.\n"); |
| return 0; |
| } |
| |
| fileDataHead.head = 0x010a0a0a; |
| fileDataHead.time = 0; |
| fileDataHead.total_len = bufSize_used; |
| |
| copy_to_user(buf, &fileDataHead, sizeof(T_SAVE_FILE_DATA)); |
| |
| readLen = ReadRingBuffer(g_debug_info_buf, (buf + sizeof(T_SAVE_FILE_DATA)), bufSize_used, ZCAT_MEM_TYPE_USER); |
| if (readLen == 0) |
| { |
| // printk("ReadRingBuffer failed.\n"); |
| return 0; |
| } |
| |
| return (readLen + sizeof(T_SAVE_FILE_DATA)); |
| } |
| |
| static int sc_debug_info_record_from_user(const char *info, unsigned short count) |
| { |
| unsigned int cnt = 0; |
| unsigned int my_jiffies = jiffies; |
| |
| if (g_init_flag == 0) |
| { |
| printk("debug_info not init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| if (info == NULL) |
| { |
| printk("sc_debug_info_record_from_user:: info is NULL.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| copy_to_user(info + 4, &my_jiffies, sizeof(my_jiffies)); |
| cnt = sc_debug_info_write(ZCAT_MEM_TYPE_USER, (UINT8 *)info, count); |
| |
| return cnt; |
| } |
| |
| module_init(debug_info_init); |
| module_exit(debug_info_exit); |
| |
| MODULE_AUTHOR("jcw"); |
| MODULE_DESCRIPTION("debug_info driver"); |
| MODULE_LICENSE("GPL"); |
| |
| |
| int sc_debug_info_vrecord(unsigned int id, const char *format, va_list args) |
| { |
| int len; |
| UINT32 writelen; |
| // va_list args; |
| char str_buf[DEBUG_INFO_MAX_TOTAL_LEN] __attribute__((aligned(4))); |
| T_SHARED_MEM_DATA *shareMemData = (T_SHARED_MEM_DATA *)str_buf; |
| |
| if (g_init_flag == 0) |
| { |
| printk("debug_info not init.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| /* args是一个char*类型指针,指向format之后的第一个参数*/ |
| // va_start(args, format); |
| len = vsnprintf(shareMemData->sub_data, DEBUG_INFO_MAX_DATA_LEN, format, args); |
| // va_end(args); |
| if (len < 0) |
| { |
| printk("vsnprintf format error.\n"); |
| return DEBUG_INFO_ERROR; |
| } |
| |
| shareMemData->module_id = (UINT16)(id & 0xFFFF); |
| shareMemData->sub_len = len; |
| shareMemData->time = jiffies; |
| |
| writelen = sc_debug_info_write(ZCAT_MEM_TYPE_KERNEL, (UINT8 *)shareMemData, len + sizeof(T_SHARED_MEM_DATA)); |
| return writelen; |
| } |
| EXPORT_SYMBOL(sc_debug_info_vrecord); |
| |
| int sc_debug_info_record(unsigned int id, const char *format, ...) |
| { |
| va_list args; |
| int r; |
| |
| va_start(args, format); |
| r = sc_debug_info_vrecord(id, format, args); |
| va_end(args); |
| |
| |
| return r; |
| } |
| EXPORT_SYMBOL(sc_debug_info_record); |
| #else |
| int sc_debug_info_record(unsigned int id, const char *format, ...) |
| { |
| return 0; |
| } |
| #endif /* _USE_ZXIC_DEBUG_INFO */ |
| |