#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>
struct proc_dir_entry *myentry;
const struct file_operations proc_mymsg_operations = {
};
static int mymsg_init(void)
{
myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
if (myentry)
myentry->proc_fops = &proc_mymsg_operations ;
return 0;
}
void mymsg_eixt(void)
{
remove_proc_entry("mymsg", &proc_root);
}
module_init(mymsg_init);
module_exit(mymsg_eixt);
所以我们接下来要做的就是来完成这个读函数,而这这个读函数里面我们要做的就是将mylog_buf中的中的数据拷贝到用户空间。有一个很关键的地方就是,我们的 mylog_buf 应该是一个环形队列,关于环形队列的概念我们先来说一下:
#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
struct proc_dir_entry *myentry;
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 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_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;
}
mylog_buf[mylog_w] = c;
mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;
/* 唤醒等待数据的进程 */
wake_up_interruptible(&mymsg_waitq);
/* 唤醒休眠的进程 */
}
/*读缓冲区:如果缓冲区为空的话,就返回0
否则从首部读出一个数据,返回1
*/
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;
}
/*打印函数:这个函数是参考sprintf函数得编写的
*它将传递进来的参数转换为固定的格式之后,放入到一个临时缓冲区里面
*然后将环形缓冲区的值写入到mylog_buf缓冲区里面,详见注释2
*/
int myprintk(const char *fmt, ...)
{
va_list args;
int i;
int j;
va_start(args, fmt);
i = vsnprintf(tmp_buf, INT_MAX, fmt, args);
//将传进来的参数转换后放入tmp_buf
va_end(args);
for (j = 0; j < i; j++)
mylog_putc(tmp_buf[j]);
//将tmp_buf里面的东东放入mylog_buf缓冲区里面
return i;
}
/*读函数:当在应用空间调用命令:cat /proc/mymsg的时候,会调用这个函数
*
*/
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 */
//如果为非阻塞且mylog_buf为空,那么就出错返回
if ((file->f_flags & O_NONBLOCK) && is_mylog_empty())
return -EAGAIN;
//如果mylog_buf为空的话进程进入等待队列,还记得我们在写缓冲区
//函数里面会唤醒进程这件事情吧!
error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());
/* copy_to_user */
//首先从缓冲区里面获得一个字符,然后拷贝到用户空间
//如果缓冲区还有信息的话,就再次获得字符,拷贝到用户
//空间,直到缓冲区为空
while (!error && (mylog_getc(&c)) && i < count) {
error = __put_user(c, buf);
//将c的内容拷贝到用户空间
buf++;
i++;
}
if (!error)
error = i;
return error;
}
const struct file_operations proc_mymsg_operations = {
.read = mymsg_read,
};
static int mymsg_init(void)
{
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);
}
module_init(mymsg_init);
module_exit(mymsg_exit);
/*因为myprintk是我们自己写的打印语句
*所以需要导出才能被使用,使用的时候还需要声明一下:
extern int myprintk(const char *fmt,...);
*/
EXPORT_SYMBOL(myprintk);
MODULE_LICENSE("GPL");
#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>
xtern int myprintk(const char *fmt, ...);
static int first_drv_init(void)
{
myprintk("first_drv_init\n");
return 0;
}
static void first_drv_exit(void)
{
myprintk("abcdefhg\n");
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");