20150429 调试分析之 imx257中proc下mymsg的实现
2015-04-29 Lover雪儿
一.实现在/proc下面创建文件条目
1.定义proc_dir_entry结构体,已经file_operatioons结构体
1 //定义proc的entry结构体 2 static struct proc_dir_entry *myentry; 3 4 static struct file_operations proc_mymsg_operations = { 5 };
2.在入口函数中创建proc条目,并且关联file_operations结构体
1 static int mymsg_init(void) 2 { 3 //创建proc的目录 4 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 5 6 if(myentry) 7 myentry->proc_fops = &proc_mymsg_operations; 8 9 return 0; 10 }
3.在出口函数中自然就是删除条目咯
1 static void mymsg_exit(void) 2 { 3 remove_proc_entry("mymsg", NULL); 4 }
4.编译测试代码,再/proc目录下创建了一个文件mymsg
附上驱动程序mymsg_1.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <linux/proc_fs.h> 9 10 //定义proc的entry结构体 11 static struct proc_dir_entry *myentry; 12 13 static struct file_operations proc_mymsg_operations = { 14 }; 15 16 static int mymsg_init(void) 17 { 18 //创建proc的目录 19 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 20 21 if(myentry) 22 myentry->proc_fops = &proc_mymsg_operations; 23 24 return 0; 25 } 26 27 static void mymsg_exit(void) 28 { 29 remove_proc_entry("mymsg", NULL); 30 } 31 32 module_init(mymsg_init); 33 module_exit(mymsg_exit); 34 35 MODULE_LICENSE("GPL"); 36 MODULE_AUTHOR("Lover雪儿");
二.实现读写函数
由于上面我们的file_operations结构体为空,所以我们自然就无法对/proc/mymsg进行读取,此处我们增加一个读函数.
1 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 2 { 3 printk("mymsg_read \n"); 4 return 0; 5 } 6 7 static struct file_operations proc_mymsg_operations = { 8 .read = mymsg_read, 9 };
编译加载完成后,我们使用cat命令对齐进行读取,结果如下所示:说明已经成功的进入了mymsg_read函数中.
附上驱动程序mymsg_2.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <linux/proc_fs.h> 9 10 //定义proc的entry结构体 11 static struct proc_dir_entry *myentry; 12 13 14 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 15 { 16 printk("mymsg_read \n"); 17 return 0; 18 } 19 20 21 static struct file_operations proc_mymsg_operations = { 22 .read = mymsg_read, 23 }; 24 25 static int mymsg_init(void) 26 { 27 //创建proc的目录 28 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 29 30 if(myentry) 31 myentry->proc_fops = &proc_mymsg_operations; 32 33 34 return 0; 35 } 36 37 static void mymsg_exit(void) 38 { 39 remove_proc_entry("mymsg", NULL); 40 } 41 42 module_init(mymsg_init); 43 module_exit(mymsg_exit); 44 45 MODULE_LICENSE("GPL"); 46 MODULE_AUTHOR("Lover雪儿");
三.模拟内存数据读取
既然要进行读取,自然就少不了数据的拷贝打印,此处我们利用数组来模拟数据的buff,然后再init函数中对其进行格式化数据,模拟写数据,
接着我们在mymsg_read函数中对其进行读取,看是否能成功读出数据.
1.定义一个内存buff数组
1 //定义proc的entry结构体 2 static struct proc_dir_entry *myentry; 3 static char mylog_buf[1024]; //数据缓冲区
2.在init函数中对其进行格式化字符串,模拟写数据
1 static int mymsg_init(void) 2 { 3 sprintf(mylog_buf, "%s", "abcdefghijklmn\n"); //模拟伪造buf的数据 4 //创建proc的目录 5 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 6 7 if(myentry) 8 myentry->proc_fops = &proc_mymsg_operations; 9 10 return 0; 11 }
3.再mymsg_read函数中对其进行读取
1 //实现读函数 2 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 3 { 4 if(copy_to_user(buf, mylog_buf, 10)); 5 return 10; 6 }
4.编译测试:发现成功的读出了数据.
附上驱动程序mymsg_3.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <linux/proc_fs.h> 9 10 //定义proc的entry结构体 11 static struct proc_dir_entry *myentry; 12 static char mylog_buf[1024]; //数据缓冲区 13 14 //实现读函数 15 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 16 { 17 //int cnt; 18 //printk("mymsg_read \n"); 19 // 把mylog_buf的数据copy_to_user, return 20 //cnt = min(1024,count); 21 if(copy_to_user(buf, mylog_buf, 10)); 22 23 return 10; 24 } 25 26 //定义file_operation结构体 27 static struct file_operations proc_mymsg_operations = { 28 .read = mymsg_read, 29 }; 30 31 static int mymsg_init(void) 32 { 33 sprintf(mylog_buf, "%s", "abcdefghijklmn\n"); //模拟伪造buf的数据 34 //创建proc的目录 35 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 36 37 if(myentry) 38 myentry->proc_fops = &proc_mymsg_operations; 39 40 return 0; 41 } 42 43 static void mymsg_exit(void) 44 { 45 remove_proc_entry("mymsg", NULL); 46 } 47 48 module_init(mymsg_init); 49 module_exit(mymsg_exit); 50 51 MODULE_LICENSE("GPL"); 52 MODULE_AUTHOR("Lover雪儿"); 53 54 55 /* 56 1.环形缓冲区 57 空: R == W 58 写: buf[W] = val; 59 W = (W+1) % 10; 60 读: val = buf[R] 61 R = (R+1) % 10 62 2. 63 64 65 66 67 68 69 70 71 72 73 */
四.参考printk的函数,可以用于对消息的进行打印保存
现在我们来编写一个类似printk的myprintk函数,从而实现其他驱动程序调用myprintk将打印信息全部输出到/proc/mymsg中,
便于统一对驱动的打印信息进行调试,不会收到其他的打印信息的干扰.
测试驱动程序我们选用以前imx257的led驱动程序:
博客文章地址:http://www.cnblogs.com/lihaiyan/p/4297923.html
当然,选择其他的驱动程序也一样,只要外部声明一下myprintk函数,然后将全部的printk替换为myprintk即可.
1.定义两个数据buff,以及读写指针,和一个等待队列
1 //定义proc的entry结构体 2 static struct proc_dir_entry *myentry; 3 static char mylog_buf[MYLOG_BUF_LEN]; //数据缓冲区 4 static char tmp_buf[MYLOG_BUF_LEN]; //数据缓冲区 5 static int mylog_r = 0; 6 static int mylog_w = 0; 7 8 static DECLARE_WAIT_QUEUE_HEAD(mylog_wait);
2.实现 判断buff空的函数
1 //判断是否为空 2 static int is_mylog_empty(void) 3 { 4 return (mylog_r == mylog_w); 5 }
3.实现 判断buff满的函数
1 //判断是否已满 2 static int is_mylog_full(void) 3 { 4 return (((mylog_w + 1) % MYLOG_BUF_LEN) == mylog_r); 5 }
4.实现 向buff写入字符的函数
1 //写入字符 2 static void mylog_putc(char c) 3 { 4 if(is_mylog_full) 5 { 6 //丢弃一个数据 7 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 8 } 9 mylog_buf[mylog_w] = c; 10 mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN; 11 12 /* 唤醒等待数据的进程 */ 13 wake_up_interruptible(&mylog_wait); 14 }
函数除了向buff中写的功能外,还有一个重要的任务就是唤醒进程,从而再read函数中将数据打印出来
5.实现 读取buff字符的函数
1 //读取字符 2 static int mylog_getc(char *p) 3 { 4 if(is_mylog_empty()) 5 return 0; 6 *p = mylog_buf[mylog_r]; 7 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 8 return 1; 9 }
6.参考内核代码中的vsprintf.c的sprinf函数,实现myprintk函数,并且导出myprintk函数,供其他的程序使用
//打印输出 参考vsprintf.c 的 sprintf int myprintk(const char *fmt, ...) { va_list args; int i,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 1; } EXPORT_SYMBOL(myprintk);
7.完善读函数
//实现读函数 参考kmsg.c static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) { int error; int i = 0; //所读的数据的个数 char c; //以非阻塞打开,并且数据队列为空 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) return -EAGAIN; //等待直到队列非空 error = wait_event_interruptible(mylog_wait,!is_mylog_empty()); /* copy_to_user 若是没错,获取字符串成功 */ while( !error && (mylog_getc(&c)) && i < count ){ error = __put_user(c, buf); //等同copy_to_user buf++; i++; } if(!error) error = i; return error; }
当应用程序使用cat来读取/proc/mymsg时,如果程序是以非阻塞的方式开始,并且buff为空的话,则直接返回,否则让程序进入可中断的睡眠,
唤醒的条件是buff不空.当程序被唤醒时,则调用copy_to_user函数将数据拷贝到用户进程中进行打印出来.
8.修改测试驱动程序err_led.c.
首先外部引入myprintk函数,然后将测试驱动程序的printk全部修改为myprintk函数.
1 extern int myprintk(const char *fmt, ...); //引入外部声明 2 3 static int key_open(struct inode *inode, struct file *file) 4 { 5 myprintk("<0>function open!\n\n"); //将printk修改未myprintk 6 return 0; 7 }
9.编译测试:
注意加载驱动的顺序.
①加载mymsg.ko驱动程序
②加载test/err_led.ko驱动程序:发现板子上的err_led灯闪烁,等led闪烁完毕
③读取 cat /proc/mymsg: 发现成功的打印出我们err_led要打印的消息,如下图
④卸载test/err_led.ko:发现板子上的err_led等闪烁,等闪烁完毕
⑤读取 cat /proc/mymsg: 再消息最末尾多了一条goodbye的消息,成功
⑥卸载mymsg.ko驱动程序
附上驱动程序mymsg_4.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <linux/proc_fs.h> 9 #include <linux/interrupt.h> 10 11 12 #define MYLOG_BUF_LEN 1024 13 14 //定义proc的entry结构体 15 static struct proc_dir_entry *myentry; 16 static char mylog_buf[MYLOG_BUF_LEN]; //数据缓冲区 17 static char tmp_buf[MYLOG_BUF_LEN]; //数据缓冲区 18 static int mylog_r = 0; 19 static int mylog_w = 0; 20 21 static DECLARE_WAIT_QUEUE_HEAD(mylog_wait); 22 23 //判断是否为空 24 static int is_mylog_empty(void) 25 { 26 return (mylog_r == mylog_w); 27 } 28 //判断是否已满 29 static int is_mylog_full(void) 30 { 31 return (((mylog_w + 1) % MYLOG_BUF_LEN) == mylog_r); 32 } 33 //写入字符 34 static void mylog_putc(char c) 35 { 36 if(is_mylog_full) 37 { 38 //丢弃一个数据 39 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 40 } 41 mylog_buf[mylog_w] = c; 42 mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN; 43 44 /* 唤醒等待数据的进程 */ 45 wake_up_interruptible(&mylog_wait); 46 } 47 //读取字符 48 static int mylog_getc(char *p) 49 { 50 if(is_mylog_empty()) 51 return 0; 52 *p = mylog_buf[mylog_r]; 53 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 54 return 1; 55 } 56 57 //打印输出 参考vsprintf.c 的 sprintf 58 int myprintk(const char *fmt, ...) 59 { 60 va_list args; 61 int i,j; 62 63 va_start(args, fmt); 64 i = vsnprintf(tmp_buf, INT_MAX, fmt, args); 65 va_end(args); 66 for(j = 0; j<i; j++) 67 { 68 mylog_putc(tmp_buf[j]); 69 } 70 return 1; 71 } 72 EXPORT_SYMBOL(myprintk); 73 74 //实现读函数 参考kmsg.c 75 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 76 { 77 int error; 78 int i = 0; //所读的数据的个数 79 char c; 80 81 //以非阻塞打开,并且数据队列为空 82 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) 83 return -EAGAIN; 84 85 //等待直到队列非空 86 error = wait_event_interruptible(mylog_wait,!is_mylog_empty()); 87 88 /* copy_to_user 若是没错,获取字符串成功 */ 89 while( !error && (mylog_getc(&c)) && i < count ){ 90 error = __put_user(c, buf); //等同copy_to_user 91 buf++; 92 i++; 93 } 94 if(!error) 95 error = i; 96 return error; 97 } 98 99 //定义file_operation结构体 100 static struct file_operations proc_mymsg_operations = { 101 .read = mymsg_read, 102 }; 103 //入口函数 104 static int mymsg_init(void) 105 { 106 //sprintf(mylog_buf, "%s", "abcdefghijklmn\n"); //模拟伪造buf的数据 107 //创建proc的条目 108 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 109 110 if(myentry) 111 myentry->proc_fops = &proc_mymsg_operations; 112 113 114 return 0; 115 } 116 //出口函数 117 static void mymsg_exit(void) 118 { 119 remove_proc_entry("mymsg", NULL); 120 } 121 122 module_init(mymsg_init); 123 module_exit(mymsg_exit); 124 125 MODULE_LICENSE("GPL"); 126 MODULE_AUTHOR("Lover雪儿"); 127 128 129 /* 130 1.环形缓冲区 131 空: R == W 132 写: buf[W] = val; 133 W = (W+1) % 10; 134 读: val = buf[R] 135 R = (R+1) % 10 136 137 138 139 */
附上测试驱动程序err_led.c:
1 #include<linux/cdev.h> 2 #include<linux/module.h> 3 #include<linux/types.h> 4 #include<linux/fs.h> 5 #include<linux/errno.h> 6 #include<linux/mm.h> 7 #include<linux/sched.h> 8 #include<linux/init.h> 9 #include<asm/io.h> 10 #include<asm/system.h> 11 #include<asm/uaccess.h> 12 #include<linux/device.h> 13 #include <linux/delay.h> 14 15 #define Driver_NAME "err_led_dev" 16 #define DEVICE_NAME "err_led_dev" 17 18 static int major = 0; 19 20 //auto to create device node 21 static struct class *drv_class = NULL; 22 static struct class_device *drv_class_dev = NULL; 23 24 //寄存器基址; 25 static unsigned long base_iomux; //iomux基址 0X 43FA C000 - 0X 43FA FFFF 26 static unsigned long base_gpio3; //gpio3 0X 53FA 4000 - 0X 53FA 7FFF 27 // MUX_CTL模式选择 配置寄存器 28 #define MUX_CTL (*(volatile unsigned long *)(base_iomux + 0x0060)) 29 // PAD_CTL GPIO常用功能设置 30 #define PAD_CTL (*(volatile unsigned long *)(base_iomux + 0x0270)) 31 // GPIO DR 数据寄存器 DR 32 #define DR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0000)) 33 // GPIO GDIR 方向控制寄存器 GDIR 34 #define GDIR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0004)) 35 36 37 extern int myprintk(const char *fmt, ...); 38 39 static int key_open(struct inode *inode, struct file *file) 40 { 41 myprintk("<0>function open!\n\n"); 42 return 0; 43 } 44 45 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 46 { 47 return 0; 48 } 49 50 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 51 { 52 myprintk("<0>function write!\n\n"); 53 return 1; 54 } 55 56 static int key_release(struct inode *inode, struct file *filp) 57 { 58 myprintk("<0>function write!\n\n"); 59 return 0; 60 } 61 62 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg) 63 { 64 myprintk("<0>function ioctl!\n\n"); 65 return 0; 66 } 67 static struct file_operations key_fops = { 68 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 69 .open = key_open, 70 .read = key_read, 71 .write = key_write, 72 .release= key_release, 73 .ioctl = key_ioctl, 74 }; 75 76 void gpio_addr(void){ 77 myprintk("<0>addr base_iomux : %x \n",base_iomux); 78 myprintk("<0>addr base_gpio3 : %x \n",base_gpio3); 79 myprintk("<0>addr MUX_CTL : %x \n",&MUX_CTL); 80 myprintk("<0>addr PAD_CTL : %x \n",&PAD_CTL); 81 myprintk("<0>addr GDIR_GPIO3 : %x \n",&GDIR_GPIO3); 82 myprintk("<0>addr DR_GPIO3 : %x \n",&DR_GPIO3); 83 } 84 85 void led_on_off(void){ 86 ssleep(1); 87 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 88 ssleep(1); 89 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 90 ssleep(1); 91 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 92 ssleep(1); 93 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 94 ssleep(1); 95 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 96 ssleep(1); 97 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 98 ssleep(1); 99 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 100 ssleep(1); 101 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 102 ssleep(1); 103 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 104 } 105 106 static int __init key_irq_init(void) 107 { 108 myprintk("<0>\nHello,this is %s module!\n\n",Driver_NAME); 109 //register and mknod 110 major = register_chrdev(0,Driver_NAME,&key_fops); 111 drv_class = class_create(THIS_MODULE,Driver_NAME); 112 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 113 114 //IO端口申请 ioremap 可以直接通过指针来访问这些地址 115 base_iomux = ioremap(0x43FAC000,0xFFF); 116 base_gpio3 = ioremap(0x53FA4000,0xFFF); 117 118 //MUX_CTL 119 MUX_CTL &= ~(0x07 << 0); 120 MUX_CTL |= (0X05 << 0); //设置为ALT5 GPIO3_23 ERR_LED 121 //PAD_CTL 122 PAD_CTL &= ~(0x01<<13 | 0x01<<3 | 0x03<<1 | 0x01<<0); //1.8v 不需要上拉下拉 CMOS输出 slew rate 123 //GDIR_GPIO3 配置为输出模式 124 GDIR_GPIO3 &= ~(0x01 << 23); 125 GDIR_GPIO3 |= (0x01 << 23); //配置为输出模式 126 127 //DR_GPIO3 配置为输出0 点亮ERR_LED 128 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 129 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 130 gpio_addr(); 131 led_on_off(); 132 return 0; 133 } 134 135 static void __exit key_irq_exit(void) 136 { 137 gpio_addr(); 138 myprintk("<0>\nGoodbye,%s!\n\n",Driver_NAME); 139 led_on_off(); 140 141 unregister_chrdev(major,Driver_NAME); 142 device_unregister(drv_class_dev); 143 class_destroy(drv_class); 144 145 //释放IO端口 146 iounmap(base_iomux); 147 iounmap(base_gpio3); 148 } 149 150 151 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 152 module_init(key_irq_init); 153 module_exit(key_irq_exit); 154 155 /* 描述驱动程序的一些信息,不是必须的 */ 156 MODULE_AUTHOR("Lover雪儿"); 157 MODULE_VERSION("0.1.0"); 158 MODULE_DESCRIPTION("IMX257 key Driver"); 159 MODULE_LICENSE("GPL");
五.完善读取,让进程读取到更加全面的内容
在前面的程序有一个问题,不知道大家发现没有,若是连续读cat /proc/mymsg的话,读的内容不全.
为了解决此问题,我们引入两套指针的方法,再次开辟一套指针用于专门读取内容
1.另外定义一套专门供读的指针
1 //定义proc的entry结构体 2 static struct proc_dir_entry *myentry; 3 static char mylog_buf[MYLOG_BUF_LEN]; //数据缓冲区 4 static char tmp_buf[MYLOG_BUF_LEN]; //数据缓冲区 5 static int mylog_r = 0; 6 static int mylog_r_forread = 0; 7 static int mylog_w = 0;
2.增加一个专门用于读取消息的判断空函数
1 //判断是否为空 2 static int is_mylog_empty(void) 3 { 4 return (mylog_r == mylog_w); 5 } 6 7 //判断是否为空 8 static int is_mylog_empty_forread(void) 9 { 10 return (mylog_r_forread == mylog_w); 11 }
3.增加一个专门用于读取消息的读取字符函数
1 //读取字符 2 static int mylog_getc(char *p) 3 { 4 if(is_mylog_empty()) 5 return 0; 6 *p = mylog_buf[mylog_r]; 7 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 8 return 1; 9 } 10 11 static int mylog_getc_forread(char *p) 12 { 13 if(is_mylog_empty_forread()) 14 return 0; 15 *p = mylog_buf[mylog_r_forread]; 16 mylog_r_forread = (mylog_r_forread + 1) % MYLOG_BUF_LEN; 17 return 1; 18 }
4.修改mymsg_read函数中的一些函数修改为专门用于读取的函数
1 //利用其他的驱动程序将printk函数写为myprintk,就会写入其中 2 3 //实现读函数 参考kmsg.c 4 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 5 { 6 int error; 7 int i = 0; //所读的数据的个数 8 char c; 9 10 //以非阻塞打开,并且数据队列为空 11 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_forread()) 12 return -EAGAIN; 13 14 //等待直到队列非空 15 error = wait_event_interruptible(mylog_wait,!is_mylog_empty_forread()); 16 17 /* copy_to_user 若是没错,获取字符串成功 */ 18 while( !error && (mylog_getc_forread(&c)) && i < count ){ 19 error = __put_user(c, buf); //等同copy_to_user 20 buf++; 21 i++; 22 } 23 if(!error) 24 error = i; 25 return error; 26 }
5.定义一个mymsg_open函数,用于重定位读指针
1 static int mymsg_open(struct inode *inode, struct file *file) 2 { 3 mylog_r_forread = mylog_r; 4 return 0; 5 } 6 7 8 //定义file_operation结构体 9 static struct file_operations proc_mymsg_operations = { 10 .read = mymsg_read, 11 .open = mymsg_open, 12 };
6.编译测试:
如下图所示:我们连续读取,所读取的内容都是全面的了
附上驱动程序mymsg_5.c
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <linux/irq.h> 7 #include <asm/uaccess.h> 8 #include <linux/proc_fs.h> 9 #include <linux/interrupt.h> 10 11 12 #define MYLOG_BUF_LEN 1024 13 14 //定义proc的entry结构体 15 static struct proc_dir_entry *myentry; 16 static char mylog_buf[MYLOG_BUF_LEN]; //数据缓冲区 17 static char tmp_buf[MYLOG_BUF_LEN]; //数据缓冲区 18 static int mylog_r = 0; 19 static int mylog_r_forread = 0; 20 static int mylog_w = 0; 21 22 static DECLARE_WAIT_QUEUE_HEAD(mylog_wait); 23 24 //判断是否为空 25 static int is_mylog_empty(void) 26 { 27 return (mylog_r == mylog_w); 28 } 29 30 //判断是否为空 31 static int is_mylog_empty_forread(void) 32 { 33 return (mylog_r_forread == mylog_w); 34 } 35 36 //判断是否已满 37 static int is_mylog_full(void) 38 { 39 return (((mylog_w + 1) % MYLOG_BUF_LEN) == mylog_r); 40 } 41 //写入字符 42 static void mylog_putc(char c) 43 { 44 if(is_mylog_full()) 45 { 46 //丢弃一个数据 47 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 48 if(((mylog_r_forread + 1) % MYLOG_BUF_LEN) == mylog_r) 49 mylog_r_forread = mylog_r; 50 } 51 mylog_buf[mylog_w] = c; 52 mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN; 53 54 /* 唤醒等待数据的进程 */ 55 wake_up_interruptible(&mylog_wait); 56 } 57 //读取字符 58 static int mylog_getc(char *p) 59 { 60 if(is_mylog_empty()) 61 return 0; 62 *p = mylog_buf[mylog_r]; 63 mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; 64 return 1; 65 } 66 67 static int mylog_getc_forread(char *p) 68 { 69 if(is_mylog_empty_forread()) 70 return 0; 71 *p = mylog_buf[mylog_r_forread]; 72 mylog_r_forread = (mylog_r_forread + 1) % MYLOG_BUF_LEN; 73 return 1; 74 } 75 76 77 //打印输出 参考vsprintf.c 的 sprintf 78 int myprintk(const char *fmt, ...) 79 { 80 va_list args; 81 int i,j; 82 83 va_start(args, fmt); 84 i = vsnprintf(tmp_buf, INT_MAX, fmt, args); 85 va_end(args); 86 for(j = 0; j<i; j++) 87 { 88 mylog_putc(tmp_buf[j]); 89 } 90 91 return 1; 92 } 93 94 //利用其他的驱动程序将printk函数写为myprintk,就会写入其中 95 96 //实现读函数 参考kmsg.c 97 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos) 98 { 99 int error; 100 int i = 0; //所读的数据的个数 101 char c; 102 103 //以非阻塞打开,并且数据队列为空 104 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_forread()) 105 return -EAGAIN; 106 107 //等待直到队列非空 108 error = wait_event_interruptible(mylog_wait,!is_mylog_empty_forread()); 109 110 /* copy_to_user 若是没错,获取字符串成功 */ 111 while( !error && (mylog_getc_forread(&c)) && i < count ){ 112 error = __put_user(c, buf); //等同copy_to_user 113 buf++; 114 i++; 115 } 116 if(!error) 117 error = i; 118 return error; 119 } 120 121 static int mymsg_open(struct inode *inode, struct file *file) 122 { 123 mylog_r_forread = mylog_r; 124 return 0; 125 } 126 127 128 //定义file_operation结构体 129 static struct file_operations proc_mymsg_operations = { 130 .read = mymsg_read, 131 .open = mymsg_open, 132 }; 133 //入口函数 134 static int mymsg_init(void) 135 { 136 //sprintf(mylog_buf, "%s", "abcdefghijklmn\n"); //模拟伪造buf的数据 137 //创建proc的条目 138 myentry = create_proc_entry("mymsg",S_IRUSR,NULL); //S_IRUSR:400 只读 139 140 if(myentry) 141 myentry->proc_fops = &proc_mymsg_operations; 142 143 144 return 0; 145 } 146 //出口函数 147 static void mymsg_exit(void) 148 { 149 remove_proc_entry("mymsg", NULL); 150 } 151 152 module_init(mymsg_init); 153 module_exit(mymsg_exit); 154 155 MODULE_LICENSE("GPL"); 156 MODULE_AUTHOR("Lover雪儿"); 157 158 EXPORT_SYMBOL(myprintk); 159 160 161 /* 162 1.环形缓冲区 163 空: R == W 164 写: buf[W] = val; 165 W = (W+1) % 10; 166 读: val = buf[R] 167 R = (R+1) % 10 168 169 170 171 */
附上测试程序err_led.c
1 #include<linux/cdev.h> 2 #include<linux/module.h> 3 #include<linux/types.h> 4 #include<linux/fs.h> 5 #include<linux/errno.h> 6 #include<linux/mm.h> 7 #include<linux/sched.h> 8 #include<linux/init.h> 9 #include<asm/io.h> 10 #include<asm/system.h> 11 #include<asm/uaccess.h> 12 #include<linux/device.h> 13 #include <linux/delay.h> 14 15 #define Driver_NAME "err_led_dev" 16 #define DEVICE_NAME "err_led_dev" 17 18 static int major = 0; 19 20 //auto to create device node 21 static struct class *drv_class = NULL; 22 static struct class_device *drv_class_dev = NULL; 23 24 //寄存器基址; 25 static unsigned long base_iomux; //iomux基址 0X 43FA C000 - 0X 43FA FFFF 26 static unsigned long base_gpio3; //gpio3 0X 53FA 4000 - 0X 53FA 7FFF 27 // MUX_CTL模式选择 配置寄存器 28 #define MUX_CTL (*(volatile unsigned long *)(base_iomux + 0x0060)) 29 // PAD_CTL GPIO常用功能设置 30 #define PAD_CTL (*(volatile unsigned long *)(base_iomux + 0x0270)) 31 // GPIO DR 数据寄存器 DR 32 #define DR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0000)) 33 // GPIO GDIR 方向控制寄存器 GDIR 34 #define GDIR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0004)) 35 36 37 extern int myprintk(const char *fmt, ...); 38 39 static int key_open(struct inode *inode, struct file *file) 40 { 41 myprintk("<0>function open!\n\n"); 42 return 0; 43 } 44 45 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 46 { 47 return 0; 48 } 49 50 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 51 { 52 myprintk("<0>function write!\n\n"); 53 return 1; 54 } 55 56 static int key_release(struct inode *inode, struct file *filp) 57 { 58 myprintk("<0>function write!\n\n"); 59 return 0; 60 } 61 62 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg) 63 { 64 myprintk("<0>function ioctl!\n\n"); 65 return 0; 66 } 67 static struct file_operations key_fops = { 68 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 69 .open = key_open, 70 .read = key_read, 71 .write = key_write, 72 .release= key_release, 73 .ioctl = key_ioctl, 74 }; 75 76 void gpio_addr(void){ 77 myprintk("<0>addr base_iomux : %x \n",base_iomux); 78 myprintk("<0>addr base_gpio3 : %x \n",base_gpio3); 79 myprintk("<0>addr MUX_CTL : %x \n",&MUX_CTL); 80 myprintk("<0>addr PAD_CTL : %x \n",&PAD_CTL); 81 myprintk("<0>addr GDIR_GPIO3 : %x \n",&GDIR_GPIO3); 82 myprintk("<0>addr DR_GPIO3 : %x \n",&DR_GPIO3); 83 } 84 85 void led_on_off(void){ 86 ssleep(1); 87 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 88 ssleep(1); 89 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 90 ssleep(1); 91 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 92 ssleep(1); 93 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 94 ssleep(1); 95 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 96 ssleep(1); 97 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 98 ssleep(1); 99 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 100 ssleep(1); 101 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 102 ssleep(1); 103 DR_GPIO3 |= (0x01 << 23); //将GPIO2_23置1 104 } 105 106 static int __init key_irq_init(void) 107 { 108 myprintk("<0>\nHello,this is %s module!\n\n",Driver_NAME); 109 //register and mknod 110 major = register_chrdev(0,Driver_NAME,&key_fops); 111 drv_class = class_create(THIS_MODULE,Driver_NAME); 112 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 113 114 //IO端口申请 ioremap 可以直接通过指针来访问这些地址 115 base_iomux = ioremap(0x43FAC000,0xFFF); 116 base_gpio3 = ioremap(0x53FA4000,0xFFF); 117 118 //MUX_CTL 119 MUX_CTL &= ~(0x07 << 0); 120 MUX_CTL |= (0X05 << 0); //设置为ALT5 GPIO3_23 ERR_LED 121 //PAD_CTL 122 PAD_CTL &= ~(0x01<<13 | 0x01<<3 | 0x03<<1 | 0x01<<0); //1.8v 不需要上拉下拉 CMOS输出 slew rate 123 //GDIR_GPIO3 配置为输出模式 124 GDIR_GPIO3 &= ~(0x01 << 23); 125 GDIR_GPIO3 |= (0x01 << 23); //配置为输出模式 126 127 //DR_GPIO3 配置为输出0 点亮ERR_LED 128 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 129 DR_GPIO3 &= ~(0x01 << 23); //将GPIO2_23清零 130 gpio_addr(); 131 led_on_off(); 132 return 0; 133 } 134 135 static void __exit key_irq_exit(void) 136 { 137 gpio_addr(); 138 myprintk("<0>\nGoodbye,%s!\n\n",Driver_NAME); 139 led_on_off(); 140 141 unregister_chrdev(major,Driver_NAME); 142 device_unregister(drv_class_dev); 143 class_destroy(drv_class); 144 145 //释放IO端口 146 iounmap(base_iomux); 147 iounmap(base_gpio3); 148 } 149 150 151 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 152 module_init(key_irq_init); 153 module_exit(key_irq_exit); 154 155 /* 描述驱动程序的一些信息,不是必须的 */ 156 MODULE_AUTHOR("Lover雪儿"); 157 MODULE_VERSION("0.1.0"); 158 MODULE_DESCRIPTION("IMX257 key Driver"); 159 MODULE_LICENSE("GPL");
好啦,大功告成.
我们这样做的好处是,可以将某一个驱动程序的打印信息集中在某个文件中,从而是打印信息不会受其他的驱动程序所影响,便于后续的调试.
接下来,我们就开始学习,根据内核打印出来的错误消息进行排错,敬请期待吧...^_^