title: 应用调试(六)记录回放输入子系统
date: 2019/1/20 23:00:39
toc: true
应用调试(六)记录回放输入子系统
思路及目标
目标: 记录我们的测试过程并复现这个流程
思路:
- 在输入子系统上报的时候,同时记录相关的操作到一个文件
- 调用这个输入文件,来上报操作
具体程序设计
-
触摸屏驱动用户点击屏幕时记录数据到
mymsg
-
模拟驱动提供两个接口
- 向上为APP提供一个接口
write
,允许用户传入一套触摸屏点击的数据 - 向下控制触摸屏,也就是根据一定的规则上报数据
- 向上为APP提供一个接口
-
APP调用模拟驱动的数据导入接口
write
,提供输入 -
APP调用模拟驱动的模拟触发功能
ioctl
,执行模拟操作
准备触摸屏驱动
找到以前写的触摸屏测试驱动,具体可以看下以前写的Linux触摸屏驱动
的测试章节
-
去除原始的触摸屏的驱动
Device Drivers-> Input device support -> Touchscreens
,去除这个驱动之后可以cat /proc/inttupts
看下有没有adc
中断,如果去除错误的话卸载驱动也有问题的之前这里出错,导致后面
cat /proc/mymsg
只有3条记录 -
内核还需要修改
LCD_mach-smdk2440.c
,这个文件里面有frambuf
的接口驱动,不修改将无法加载lcd
驱动程序,然后去除lcd
模块Device Drivers>Graphics support
编译为模块(M
选项),重新编译上节课的lcd驱动,再编译模块make modules
-
编译自己写的触摸屏驱动,液晶驱动(需要使用4.3的资源文件),需要重新使用内核的
cfbxxx.ko
,否则段错误 -
使用
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. 上次上报时间与当前读取到的时间相同,直接上报,否则等待下次定时时间到
...
}
测试
- 卸载原有的
mymsg
驱动,防止有数据 - 加载驱动
- 运行
ts_test
- 这个时候查看下
cat proc/mymsg
是否有一些无效数据,老师的原来的触摸屏驱动是有的,所以需要标记一个tag
,然后自己手动删除掉tag
前的文字记录,我自己优化的不需要了,没有多余的数据 - 标记
tag
,老师的代码需要,改进后的不需要了 - 手动点击触摸屏,写个字
- 复制这个文件到一个txt
- 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