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

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

思路及目标

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

思路:

  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!\n");
		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] = '\0';
			myprintk("%s\n", 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] == '\n') || (simulate_buf[pt_read] == '\r') || (simulate_buf[pt_read] == '\t'))
			pt_read++;
		else
			break;
	}

	while (pt_read <= pt_write)
	{
		if ((simulate_buf[pt_read] == '\n') || (simulate_buf[pt_read] == '\r'))
			break;
		else
		{
			line[i] = simulate_buf[pt_read];
			pt_read++;	
			i++;
		}
	}

	line[i] = '\0';
	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\n");
			del_timer(&simulate_timer);
			pre_time = pre_type = pre_code = 0;
			pre_val = 0;
			pt_read = pt_write = 0;
			break;
		}
        //printk("hello\n");
        /* 处理数据 */
		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 \n");
		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\n",++cnt);
	myprintk("0x%08x 0x%08x 0x%08x %d\n", 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\r\n",x,y);
    }
    else
    {
        printk("now is up\r\n");
        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\r\n",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\r\n");
    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 \r\n");

    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 \r\n");
}

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:\n");
	printf("%s write <file>\n", file);
	printf("%s replay\n", file);
	printf("%s tag <string>\n", 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\n");
		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\n", argv[2]);
			return -1;
		}

		while (1)
		{
			len = read(fd_data, buf, 100);
			if (len == 0)
			{
				printf("wite ok\n");
				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\n", __FUNCTION__, __LINE__);
	//printk("count = %d\n", count);
	//printk("mylog_r = %d\n", mylog_r);
	//printk("mylog_w = %d\n", mylog_w);

	error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());

	//printk("%s %d\n", __FUNCTION__, __LINE__);
	//printk("count = %d\n", count);
	//printk("mylog_r = %d\n", mylog_r);
	//printk("mylog_w = %d\n", 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\n");
		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

posted @ 2019-01-21 14:11  zongzi10010  阅读(173)  评论(0编辑  收藏  举报