zoukankan      html  css  js  c++  java
  • 应用调试(六)记录回放输入子系统


    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

  • 相关阅读:
    专职DBA-MySQL体系结构与基本管理
    JSON
    MIME类型
    文件上传下载
    response常用的方法
    2020.11.27小记
    HTTP请求状态码
    1561. Maximum Number of Coins You Can Get
    1558. Minimum Numbers of Function Calls to Make Target Array
    1557. Minimum Number of Vertices to Reach All Nodes
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10298428.html
Copyright © 2011-2022 走看看