• 应用调试(六)记录回放输入子系统



    title: 应用调试(六)记录回放输入子系统
    date: 2019/1/20 23:00:39
    toc: true

    应用调试(六)记录回放输入子系统

    思路及目标

    目标: 记录我们的测试过程并复现这个流程

    思路:

    1. 在输入子系统上报的时候,同时记录相关的操作到一个文件
    2. 调用这个输入文件,来上报操作

    具体程序设计

    1. 触摸屏驱动用户点击屏幕时记录数据到mymsg

    2. 模拟驱动提供两个接口

      1. 向上为APP提供一个接口write,允许用户传入一套触摸屏点击的数据
      2. 向下控制触摸屏,也就是根据一定的规则上报数据
    3. APP调用模拟驱动的数据导入接口write,提供输入

    4. APP调用模拟驱动的模拟触发功能ioctl,执行模拟操作

    准备触摸屏驱动

    找到以前写的触摸屏测试驱动,具体可以看下以前写的Linux触摸屏驱动的测试章节

    1. 去除原始的触摸屏的驱动Device Drivers-> Input device support -> Touchscreens,去除这个驱动之后可以cat /proc/inttupts看下有没有adc中断,如果去除错误的话卸载驱动也有问题的

      之前这里出错,导致后面cat /proc/mymsg 只有3条记录

      mark

      mark

    2. 内核还需要修改LCD_mach-smdk2440.c,这个文件里面有frambuf的接口驱动,不修改将无法加载lcd驱动程序,然后去除lcd模块Device Drivers>Graphics support 编译为模块(M选项),重新编译上节课的lcd驱动,再编译模块make modules

      mark

    3. 编译自己写的触摸屏驱动,液晶驱动(需要使用4.3的资源文件),需要重新使用内核的cfbxxx.ko,否则段错误

    4. 使用ts_lib测试,注意这里参数export TSLIB_TSDEVICE=/dev/event0,之前使用了event1导致触摸没反应只有现实

    #主机
    # 编译lcd 触摸屏 内核 内核模块 复制模块到nfs
    cp ~/stu/kernel/linux-2.6.22.6/drivers/video/cfb*.ko .
    
    #如果要使用nfs启动 
    ###set bootargs noinitrd root=/dev/nfs nfsroot=192.168.95.222:/home/book/stu/fs/4th/ ip=192.168.95.200:192.168.95.222:192.168.95.222:255.255.255.0::eth0:off  init=/linuxrc console=ttySAC0 user_debug=0xff
    
    #单板
    #去除qt
    vi /etc/init.d/rcS
    reboot
    mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt
    cd /mnt/code
    #cfb 液晶 触摸屏
    insmod lcd/cfbcopyarea.ko  && insmod  lcd/cfbfillrect.ko  && insmod lcd/cfbimgblt.ko  && insmod lcd/lcd.ko && insmod dri.ko
    # 配置tslib参数
    export TSLIB_TSDEVICE=/dev/event0 && export TSLIB_CALIBFILE=/etc/pointercal && export TSLIB_CONFFILE=/etc/ts.conf  && export TSLIB_PLUGINDIR=/lib/ts  && export TSLIB_CONSOLEDEVICE=none  && export TSLIB_FBDEVICE=/dev/fb0
    
    #测试
    ts_calibrate
    ts_test
    

    模拟驱动接口

    保存输入到文件

    我们再上报数据的时候,调用我们自己的myprintf,首先扩充mymsg的大小为1M,使用kmalloc() 申请内存,它的内存在物理上也是连续的,退出的时候使用kfree释放.

    修改触摸屏驱动,这里我的触摸屏驱动与老师的优点不一样,上报数据只在一个函数内两个地方,比较好修改,存储的时候加入时间戳即可.具体的代码见最后的附录.

    //松开
    input_report_abs(g_input_dev, ABS_PRESSURE, 0);
    write_input_event_to_file(jiffies, EV_ABS, ABS_PRESSURE, 0);
    input_report_key(g_input_dev, BTN_TOUCH, 0);
    write_input_event_to_file(jiffies, EV_KEY, BTN_TOUCH, 0);
    input_sync(g_input_dev);
    write_input_event_to_file(jiffies, EV_SYN, SYN_REPORT, 0);
    
    //按下
    input_report_abs(g_input_dev, ABS_X, g_adc_date.x);
    write_input_event_to_file(jiffies, EV_ABS, ABS_X, g_adc_date.x);
    input_report_abs(g_input_dev, ABS_Y, g_adc_date.y);
    write_input_event_to_file(jiffies, EV_ABS, ABS_Y, g_adc_date.y);
    input_report_abs(g_input_dev, ABS_PRESSURE, 1);
    write_input_event_to_file(jiffies, EV_ABS, ABS_PRESSURE, 1);
    input_report_key(g_input_dev, BTN_TOUCH, 1);
    write_input_event_to_file(jiffies, EV_KEY, BTN_TOUCH, 1);
    input_sync(g_input_dev);
    write_input_event_to_file(jiffies, EV_SYN, SYN_REPORT, 0);
    

    数据导入

    这个接口比较简单,就是读取用户提供的文件,保存到内存buff中即可

    static ssize_t simulate_write(struct file * file, const char __user *buf, size_t size, loff_t *offset)
    

    启动回放

    开启定时器,然后定时器函数里面去读取已经被导入的buff

    static int simulate_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
    

    定时器函数

    读取每一行,分析数据,如果到了上报时间则上报输入子系统

    static void simulate_time_fun(unsigned long data)
    {
    	1. 读取一行
        2. 如果是第一次读取则直接上报
        3. 上次上报时间与当前读取到的时间相同,直接上报,否则等待下次定时时间到
            ...
    }
    

    测试

    1. 卸载原有的mymsg驱动,防止有数据
    2. 加载驱动
    3. 运行ts_test
    4. 这个时候查看下cat proc/mymsg是否有一些无效数据,老师的原来的触摸屏驱动是有的,所以需要标记一个tag,然后自己手动删除掉tag前的文字记录,我自己优化的不需要了,没有多余的数据
    5. 标记tag,老师的代码需要,改进后的不需要了
    6. 手动点击触摸屏,写个字
    7. 复制这个文件到一个txt
    8. app写入这个txt到内部缓存,然后运行
    # rmmod dri && rmmod mymsg
    goodbye, remod
    # insmod mymsg/mymsg.ko && insmod dri.ko
    hello, insmod
    input: Unspecified device as /class/input/input6
    # ts_test &
    # cat /proc/mymsg
    #  屏幕这里没有输出,说明自己写的代码不需要tag
    
    # 在这里操作触摸屏
    
    # 这里复制文件好像有点问题,应该是mymsg没做好,读不到文件结束符
    # cp /proc/mymsg  log.txt
    
    
    # kill -9  xxx # 关掉之前的ts_test
    # ts_test & 清个屏
    
    # ./app write log.txt
    wite ok
    # ./app replay
    
    # 一次性卸载驱动重新加载驱动
    # rmmod dri && rmmod mymsg &&  insmod mymsg/mymsg.ko && insmod drv_bak.ko  &&  ts_test &
    

    完整程序

    模拟驱动

    模拟驱动的函数直接加入到原来的触摸屏驱动中,具体见代码注释

    #include <linux/errno.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/input.h>
    #include <linux/init.h>
    #include <linux/serio.h>
    #include <linux/delay.h>
    #include <linux/platform_device.h>
    #include <linux/clk.h>
    #include <asm/io.h>
    #include <asm/irq.h>
    #include <asm/plat-s3c24xx/ts.h>
    #include <asm/arch/regs-adc.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/uaccess.h>
    extern int myprintk(const char *fmt, ...);
    struct input_dev *g_input_dev;
    static struct timer_list touch_timer;
    #define MAX_ADC_CNT  20
    static struct adc_date
    {
        int _x[MAX_ADC_CNT];
        int _y[MAX_ADC_CNT];
        unsigned long x;
        unsigned long y;
        int cnt;
    } g_adc_date;
    struct s3c2440_adc_reg
    {
        unsigned long adccon;
        unsigned long adctsc;
        unsigned long adcdly;
        unsigned long adcdat0;
        unsigned long adcdat1;
        unsigned long adcupdn;
    };
    static volatile struct s3c2440_adc_reg *g_s3c2440_adc_reg;
    static int time_enable = 0;
    
    
    /********************************************************************
    *
    *							模拟驱动
    *
    *******************************************************************/
    #define SIMULATE_BUF_LEN    (1024*1024)
    static unsigned char * simulate_buf;
    static unsigned int pt_read=0,pt_write=0;
    static int simulate_major = 0;
    static struct class * simulate_cls;
    static struct timer_list simulate_timer;
    
    static ssize_t simulate_write(struct file * file, const char __user *buf, size_t size, loff_t *offset)
    {
    	int err;
    	if (pt_write + size >= SIMULATE_BUF_LEN)
    	{
    		printk("SIMULATE_BUF  is  full!
    ");
    		return -EIO;
    	}
    	
    	err = copy_from_user(simulate_buf + pt_write, buf, size);
    	if (err)
    	{
    		return -EIO;
    	}
    	else
    	{
    		pt_write += size;
    	}
    	return size;
    }
    
    #define INPUT_REPLAY   0
    #define INPUT_TAG      1
    /* app: ioctl(fd, CMD, ..); */
    static int simulate_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
    {
    	char buf[100];
    	switch (cmd)
    	{
    		case INPUT_REPLAY:
    		{
    			/* 启动回放: 根据replay_buf里的数据来上报事件 */
    			simulate_timer.expires = jiffies + 1;
    			add_timer(&simulate_timer);
    			break;
    		}
    		case INPUT_TAG:
    		{
    			copy_from_user(buf, (const void __user *)arg, 100);
    			buf[99] = '';
    			myprintk("%s
    ", buf);
    			break;
    		}
    	}	
    	return 0;
    }
    
    /* 返回值: 0 - 无数据 */
    static int replay_get_line(char *line)
    {
    	int i = 0;
    	
    	/* 吃掉前导的空格、回车符 */
    	while (pt_read <= pt_write)
    	{
    		if ((simulate_buf[pt_read] == ' ') || (simulate_buf[pt_read] == '
    ') || (simulate_buf[pt_read] == '
    ') || (simulate_buf[pt_read] == '	'))
    			pt_read++;
    		else
    			break;
    	}
    
    	while (pt_read <= pt_write)
    	{
    		if ((simulate_buf[pt_read] == '
    ') || (simulate_buf[pt_read] == '
    '))
    			break;
    		else
    		{
    			line[i] = simulate_buf[pt_read];
    			pt_read++;	
    			i++;
    		}
    	}
    
    	line[i] = '';
    	return i;	
    }
    
    static void simulate_time_fun(unsigned long data)
    {
    	unsigned int time;
    	unsigned int type;
    	unsigned int code;
    	int val;
    	static unsigned int pre_time = 0, pre_type = 0, pre_code = 0;
    	static int pre_val = 0;
    	char line[100];
    	int ret;
        static int is_first=1,is_new_line=0;
    
        if (is_new_line )
    	{
    		input_event(g_input_dev, pre_type, pre_code, pre_val);
    	}
    	while (1)
    	{  
    		ret = replay_get_line(line);
    		if (ret == 0)
    		{
    			printk("end of input replay
    ");
    			del_timer(&simulate_timer);
    			pre_time = pre_type = pre_code = 0;
    			pre_val = 0;
    			pt_read = pt_write = 0;
    			break;
    		}
            //printk("hello
    ");
            /* 处理数据 */
    		time = 0;
    		type = 0;
    		code = 0;
    		val  = 0;
    		sscanf(line, "%x %x %x %d", &time, &type, &code, &val);
    		if (!time && !type && !code && !val)
    			continue;
            else
            {
    
                if (is_first )
                {
                    input_event(g_input_dev, type, code, val);
                    is_first=0;
                    pre_time=time;
                }else if (pre_time==time) 
                {
                    input_event(g_input_dev, type, code, val);
                }
                else
                {
                    mod_timer(&simulate_timer, jiffies + (time - pre_time));
                    pre_time = time;
    				pre_type = type;
    				pre_code = code;
    				pre_val  = val;
                    is_new_line=1;
                    break;
                }
            }
        }
    }
    static struct file_operations simulate_ops = {
    	.owner   = THIS_MODULE,
    	.write   = simulate_write,
    	.ioctl   = simulate_ioctl,
    };
    int simulate_init(void)
    {
        simulate_buf = kmalloc(SIMULATE_BUF_LEN, GFP_KERNEL);
    	if (!simulate_buf)
    	{
    		printk("kmalloc for simulate buff failed 
    ");
    		return -1;
    	}
        else
        {
        	simulate_major = register_chrdev(0, "dev_simulate", &simulate_ops);
        	simulate_cls = class_create(THIS_MODULE, "simulate_class");
        	device_create(simulate_cls, NULL, MKDEV(simulate_major, 0), "input_simulate"); /* /dev/input_simulate */
        	init_timer(&simulate_timer);
        	simulate_timer.function = simulate_time_fun;
            return  0;
        }
    }
    void simulate_exit(void)
    {
    	kfree(simulate_buf);
    	device_destroy(simulate_cls, MKDEV(simulate_major, 0));
    	class_destroy(simulate_cls);
    	unregister_chrdev(simulate_major, "dev_simulate");
    }
    
    
    /********************************************************************
    *
    *							原有的触摸屏驱动
    *
    *******************************************************************/
    #define MODE_ADC_START   0   //get X and Y adc
    #define MODE_WAIT_DOWN 1
    #define MODE_WAIT_UP       2
    #define MODE_WAIT_IRQ      3
    static void __inline select_mode(int mode)
    {
        time_enable = 0;
        switch (mode)
        {
        case MODE_ADC_START:
        {
            g_s3c2440_adc_reg->adctsc = (1 << 3) | (1 << 2); // mode select
            g_s3c2440_adc_reg->adccon |= (1 << 0);  //start adc
            break;
        }
        case MODE_WAIT_DOWN:
        {
            g_s3c2440_adc_reg->adctsc = 0xd3; // mode select
            break;
        }
        case MODE_WAIT_UP:
        {
            g_s3c2440_adc_reg->adctsc = 0x1d3; // mode select
            break;
        }
        case MODE_WAIT_IRQ:
        {
            g_s3c2440_adc_reg->adctsc |= 0x03; // mode select
            break;
        }
        default:
        {
            printk("mode is err,please check");
            break;
        }
        }
    }
    
    void write_input_event_to_file(unsigned int time, unsigned int type, unsigned int code, int val)
    {
        //static int cnt=0;
        //printk("now is to write file %d
    ",++cnt);
    	myprintk("0x%08x 0x%08x 0x%08x %d
    ", time, type, code, val);	
    }
    /**
     * @param x
     * @param y
     * @param isdown  1=down,0=up
     *
     * @return  1 =fifo is full
     */
    static int put_adc(int x, int y, int isdown)
    {
        int i = 0;
        if (isdown)
        {
            g_adc_date._x[g_adc_date.cnt] = x;
            g_adc_date._y[g_adc_date.cnt] = y;
            //printk("x=%d,y=%d
    ",x,y);
        }
        else
        {
            printk("now is up
    ");
            input_report_abs(g_input_dev, ABS_PRESSURE, 0);
            write_input_event_to_file(jiffies, EV_ABS, ABS_PRESSURE, 0);
            input_report_key(g_input_dev, BTN_TOUCH, 0);
            write_input_event_to_file(jiffies, EV_KEY, BTN_TOUCH, 0);
            input_sync(g_input_dev);
            write_input_event_to_file(jiffies, EV_SYN, SYN_REPORT, 0);
            g_adc_date.cnt = 0;
            return 0;
        }
        g_adc_date.cnt++;
        if (g_adc_date.cnt >= MAX_ADC_CNT)
        {
            g_adc_date.x = 0;
            g_adc_date.y = 0;
            for (i = 0; i < MAX_ADC_CNT; i++)
            {
                g_adc_date.x += g_adc_date._x[i];
                g_adc_date.y += g_adc_date._y[i];
            }
            g_adc_date.x /= MAX_ADC_CNT;
            g_adc_date.y /= MAX_ADC_CNT;
    
            printk("x=%d,y=%d
    ",g_adc_date.x,g_adc_date.y);
            input_report_abs(g_input_dev, ABS_X, g_adc_date.x);
            write_input_event_to_file(jiffies, EV_ABS, ABS_X, g_adc_date.x);
    
            input_report_abs(g_input_dev, ABS_Y, g_adc_date.y);
            write_input_event_to_file(jiffies, EV_ABS, ABS_Y, g_adc_date.y);
            input_report_abs(g_input_dev, ABS_PRESSURE, 1);
            write_input_event_to_file(jiffies, EV_ABS, ABS_PRESSURE, 1);
            input_report_key(g_input_dev, BTN_TOUCH, 1);
            write_input_event_to_file(jiffies, EV_KEY, BTN_TOUCH, 1);
            input_sync(g_input_dev);
            write_input_event_to_file(jiffies, EV_SYN, SYN_REPORT, 0);
            g_adc_date.cnt = 0;
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
    static irqreturn_t irq_adc_ts_fun ( int irq, void *dev_id)
    {
        //printk("get in pendown irq
    ");
        select_mode(MODE_WAIT_IRQ);
        if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
        {
            put_adc(0, 0, 0);
            select_mode(MODE_WAIT_DOWN);
        }
        else
        {
            //  ignore the first adc data for pur_adc ()
            select_mode(MODE_ADC_START);
        }
        return IRQ_HANDLED;
    }
    
    static irqreturn_t irq_adc_get_fun ( int irq, void *dev_id)
    {
        int x = (g_s3c2440_adc_reg->adcdat0)&0x3FF;
        int y = (g_s3c2440_adc_reg->adcdat1)&0x3FF;
    
        select_mode(MODE_WAIT_IRQ);
        if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
        {
            put_adc(0, 0, 0);
            select_mode(MODE_WAIT_DOWN);
        }
        else
        {
            if(put_adc(x, y, 1))
            {
                // fifo is full ,hold to sleep for ms
                select_mode(MODE_WAIT_UP);
                time_enable = 1;
                mod_timer(&touch_timer, jiffies + HZ/100);
            }
            else
            {
                select_mode(MODE_ADC_START);
            }
        }
    
        return IRQ_HANDLED;
    }
    static void irq_time_fun(unsigned long data)
    {
        if (!time_enable) return ;
        select_mode(MODE_WAIT_IRQ);
        if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
        {
            put_adc(0, 0, 0);
            select_mode(MODE_WAIT_DOWN);
        }
        else
        {
            select_mode(MODE_ADC_START);
        }
    }
    
    
    static int __init dri_init(void)
    {
        printk("hello, insmod 
    ");
    
        if (simulate_init()) {
            return EIO;
        }
    
        // allocate a memory for input_dev
        g_input_dev = input_allocate_device();
        //set the input_dev
        //设置分两类:产生哪类事件;产生这类事件中的哪些具体事件〿    // event lever class
        //set_bit(EV_SYN, g_input_dev->evbit);
        set_bit(EV_KEY, g_input_dev->evbit);
        set_bit(EV_ABS, g_input_dev->evbit);
        // event lever bit
        set_bit(BTN_TOUCH, g_input_dev->keybit);
        input_set_abs_params(g_input_dev, ABS_X, 0, 0x3FF, 0, 0);   //2440 adcbit=10=1024
        input_set_abs_params(g_input_dev, ABS_Y, 0, 0x3FF, 0, 0);
        input_set_abs_params(g_input_dev, ABS_PRESSURE, 0, 1, 0, 0);//touch or release
        //registe the input_dev
        input_register_device(g_input_dev);
    
        /**
         * hardware set
         * 1. set clock
         * 2. set othee regs
        */
        {
            struct clk *clk;
            clk = clk_get(NULL, "adc");
            clk_enable(clk);
        }
    
        g_s3c2440_adc_reg = ioremap(0x58000000, sizeof(struct s3c2440_adc_reg));
        /* bit[14]  : 1-A/D converter prescaler enable
         * bit[13:6]: A/D converter prescaler value,
         *            49, ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz
         * bit[0]: A/D conversion starts by enable. 先设丿
         */
        g_s3c2440_adc_reg->adccon = (1 << 14) | (49 << 6);
        // max delay for adc
        g_s3c2440_adc_reg->adcdly = 0xffff;
    
        request_irq(IRQ_ADC, irq_adc_get_fun, IRQF_SAMPLE_RANDOM, "adc_get", NULL);
        request_irq(IRQ_TC, irq_adc_ts_fun, IRQF_SAMPLE_RANDOM, "adc_ts", NULL);
        init_timer(&touch_timer);
        touch_timer.function = irq_time_fun;
        add_timer(&touch_timer);
    
        memset(&g_adc_date, 0x00, sizeof(g_adc_date));
    
        select_mode(MODE_WAIT_DOWN);
        return 0;
    }
    
    static void __exit dri_exit(void)
    {
        simulate_exit();
        free_irq(IRQ_TC, NULL);
        free_irq(IRQ_ADC, NULL);
        iounmap(g_s3c2440_adc_reg);
        input_unregister_device(g_input_dev);
        input_free_device(g_input_dev);
        del_timer(&touch_timer);
        printk("goodbye, remod 
    ");
    }
    
    module_init(dri_init);
    module_exit(dri_exit);
    MODULE_LICENSE("GPL");
    
    
    

    APP

    这里使用方式如下

    ./app write 1.txt  	#写缓存
    ./app replay		#回放
    ./app tag start		#老师原来的触摸屏驱动需要这个,优化后的不需要
    

    程序直接是老师的

    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <poll.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define INPUT_REPLAY   0
    #define INPUT_TAG      1
    
    /* Usage:
     * ./input_replay write <file>
     * ./input_replay replay
     * ./input_repaly tag <string>
     */
    
    void print_usage(char *file)
    {
    	printf("Usage:
    ");
    	printf("%s write <file>
    ", file);
    	printf("%s replay
    ", file);
    	printf("%s tag <string>
    ", file);
    }
    
    int main(int argc, char **argv)
    {
    	int fd;
    	int fd_data;
    	int buf[100];
    	int len;
    	
    	if (argc != 2 && argc != 3)
    	{
    		print_usage(argv[0]);
    		return -1;
    	}
    
    	fd = open("/dev/input_simulate", O_RDWR);
    	if (fd < 0)
    	{
    		printf("can't open /dev/input_simulate
    ");
    		return -1;
    	}
    
    	if (strcmp(argv[1], "replay") == 0)
    	{
    		ioctl(fd, INPUT_REPLAY);
    	}
    	else if (strcmp(argv[1], "write") == 0)
    	{
    		if (argc != 3)
    		{
    			print_usage(argv[0]);
    			return -1;
    		}
    
    		fd_data = open(argv[2], O_RDONLY);
    		if (fd_data < 0)
    		{
    			printf("can't open %s
    ", argv[2]);
    			return -1;
    		}
    
    		while (1)
    		{
    			len = read(fd_data, buf, 100);
    			if (len == 0)
    			{
    				printf("wite ok
    ");
    				break;
    			}
    			else
    			{
    				write(fd, buf, len);				
    			}
    		}
    	}
    	else if (strcmp(argv[1], "tag") == 0)
    	{
    		if (argc != 3)
    		{
    			print_usage(argv[0]);
    			return -1;
    		}
    		ioctl(fd, INPUT_TAG, argv[2]);
    	}
    	else
    	{
    		print_usage(argv[0]);
    		return -1;
    	}
    
    	return 0;
    	
    }
    

    mymsg

    这里使用读后清除与读后不清除应该都可以,实验中用的是读后不清除,所以需要每次重新做的时候卸载驱动mymsg

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <linux/proc_fs.h>
    
    #define MYLOG_BUF_LEN (1024*1024)
    
    struct proc_dir_entry *myentry;
    
    //static char mylog_buf[MYLOG_BUF_LEN];
    //static char tmp_buf[MYLOG_BUF_LEN];
    
    char *mylog_buf;
    char tmp_buf[1024];
    
    static int mylog_r = 0;
    static int mylog_r_for_read = 0;
    static int mylog_w = 0;
    
    static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);
    
    static int is_mylog_empty(void)
    {
    	return (mylog_r == mylog_w);
    }
    
    static int is_mylog_empty_for_read(void)
    {
    	return (mylog_r_for_read == mylog_w);
    }
    
    static int is_mylog_full(void)
    {
    	return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
    }
    
    static void mylog_putc(char c)
    {
    	if (is_mylog_full())
    	{
    		/* 丢弃一个数据 */
    		mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
    
    		if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)
    		{
    			mylog_r_for_read = mylog_r;
    		}
    	}
    
    	mylog_buf[mylog_w] = c;
    	mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;
    
    	/* 唤醒等待数据的进程 */	
        wake_up_interruptible(&mymsg_waitq);   /* 唤醒休眠的进程 */	
    }
    
    static int mylog_getc(char *p)
    {
    	if (is_mylog_empty())
    	{
    		return 0;
    	}
    	*p = mylog_buf[mylog_r];
    	mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
    	return 1;
    }
    
    static int mylog_getc_for_read(char *p)
    {
    	if (is_mylog_empty_for_read())
    	{
    		return 0;
    	}
    	*p = mylog_buf[mylog_r_for_read];
    	mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;
    	return 1;
    }
    
    
    int myprintk(const char *fmt, ...)
    {
    	va_list args;
    	int i;
    	int j;
    
    	va_start(args, fmt);
    	i = vsnprintf(tmp_buf, INT_MAX, fmt, args);
    	va_end(args);
    	
    	for (j = 0; j < i; j++)
    		mylog_putc(tmp_buf[j]);
    		
    	return i;
    }
    
    static ssize_t mymsg_read(struct file *file, char __user *buf,
    			 size_t count, loff_t *ppos)
    {
    	int error = 0;
    	int i = 0;
    	char c;
    
    	/* 把mylog_buf的数据copy_to_user, return */
    	if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())
    		return -EAGAIN;
    
    	//printk("%s %d
    ", __FUNCTION__, __LINE__);
    	//printk("count = %d
    ", count);
    	//printk("mylog_r = %d
    ", mylog_r);
    	//printk("mylog_w = %d
    ", mylog_w);
    
    	error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
    
    	//printk("%s %d
    ", __FUNCTION__, __LINE__);
    	//printk("count = %d
    ", count);
    	//printk("mylog_r = %d
    ", mylog_r);
    	//printk("mylog_w = %d
    ", mylog_w);
    
    	/* copy_to_user */
    	while (!error && (mylog_getc_for_read(&c)) && i < count) {
    		error = __put_user(c, buf);
    		buf++;
    		i++;
    	}
    	
    	if (!error)
    		error = i;
    	
    	return error;
    }
    
    static int mymsg_open(struct inode *inode, struct file *file)
    {
    	mylog_r_for_read = mylog_r;
    	return 0;
    }
    
    const struct file_operations proc_mymsg_operations = {
    	.open = mymsg_open,
    	.read = mymsg_read,
    };
    
    static int mymsg_init(void)
    {	
    	mylog_buf = kmalloc(MYLOG_BUF_LEN, GFP_KERNEL);
    	if (!mylog_buf)
    	{
    		printk("can't alloc for mylog_buf
    ");
    		return -EIO;
    	}
    	
    	myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
    	if (myentry)
    		myentry->proc_fops = &proc_mymsg_operations;
    	return 0;
    }
    
    static void mymsg_exit(void)
    {
    	remove_proc_entry("mymsg", &proc_root);
    	kfree(mylog_buf);
    }
    
    module_init(mymsg_init);
    module_exit(mymsg_exit);
    EXPORT_SYMBOL(myprintk);
    MODULE_LICENSE("GPL");
    

    代码下载GIT

    https://gitee.com/layty/Jz2440/tree/master/Driver/code/38th-input-simulate

  • 相关阅读:
    01. 容器技术
    02. 关于 docker
    MAUI + MVVM + SIEMENS 跨平台应用实战
    从单例谈doublecheck必要性,多种单例各取所需
    git创建远程分支
    fabric peer节点账本验证器相关代码解读
    git 忽略 .idea文件
    Machine Learning With Go 第4章:回归
    一个恢复CSI挂载信息的解决方法
    使用kubeseal加密和管理k8s集群的secret
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10298428.html
Copyright © 2020-2023  润新知