分类: 嵌入式
2015-05-25 20:16:22
first_drv 笔记
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 <asm/uaccess.h>
7
#include <asm/irq.h>
8
#include <asm/io.h>
9
#include <asm/arch/regs-gpio.h>
10
#include <asm/hardware.h>
11
12 static int first_drv_open(struct
inode *inode, struct file *file){
13
printk("first_drv_open!\n");
14
return 0;
15
}
16
17 static ssize_t first_drv_write(struct file *file, const char __user
*buf, size_t count, loff_t * ppos){
18
printk("first_drv_write\n");
19
return 0;
20
}
21
22 static struct file_operations
first_drv_fops = {
23
.owner = THIS_MODULE,
24
.open = first_drv_open,
25
.write = first_drv_write,
26
};
27
28
int major;
29 static int __init first_drv_init(void){
30
major = register_chrdev(0, "first_drv",
&first_drv_fops);
31
return 0;
32
}
33
34 static void __exit
first_drv_exit(void){
35
unregister_chrdev(major, "first_drv");
36
}
39
40
module_init(first_drv_init);
41
module_exit(first_drv_exit);
42
43
MODULE_LICENSE("GPL");
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 <asm/uaccess.h>
7
#include <asm/irq.h>
8
#include <asm/io.h>
9
#include <asm/arch/regs-gpio.h>
10
#include <asm/hardware.h>
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 <asm/uaccess.h>
7
#include <asm/irq.h>
8
#include <asm/io.h>
9
#include <asm/arch/regs-gpio.h>
10
#include <asm/hardware.h>
1
#include <sys/types.h>
2
#include <sys/stat.h>
3
#include <fcntl.h>
4
#include <stdio.h>
15年4月27日19:03:23
第一步:自动分配主设备号。
驱动代码如下:
******************************************************************************
******************************************************************************
测试程序如下:
(1)驱动程序跟应用程序不同,没有main主函数,当安装设备时,就调用init函数,即29行的函数。当卸载设备的时候,调用exit函数即可。
入口函数init:注册设备,需要知道设备的主设备号,名称,对应的file_operation结构体指针。
同样,出口函数exit中就需要注销设备,知道主设备号,名称即可。
(2)设备不需要知道底层的东西,当要打开东西的时候,它只需要调用open函数即可,至于open函数需要怎么做,就是驱动的任务,咱们则只打印了一句话。
(3)还必须把file_operation这个结构体填充完,字符驱动函数基本就是围绕这个结构体来写的,并带上出入口函数。
======================================================================
第二步:当驱动程序选择自动创建主设备号的时候,主设备号可能一直发生变化,这时候应用程序就必须一直cat /proc/devices来查看主设备号,同时更改应用程序中的设备号。这样做很麻烦。
用busybox创建的根文件系统中有一个mdev机制,它可以根据系统信息自动创建设备结点。下面就是这种机制:
驱动代码:
12 static struct class *first_drv_class;
13 static struct class_device *first_drv_class_dev;
14
15 static int first_drv_open(struct inode *inode, struct file *file){
16 printk("first_drv_open!\n");
17 return 0;
18 }
19
20 static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){
21 printk("first_drv_write\n");
22 return 0;
23 }
24
25 static struct file_operations first_drv_fops = {
26 .owner = THIS_MODULE,
27 .open = first_drv_open,
28 .write = first_drv_write,
29 };
30
31 int major;
32 static int __init first_drv_init(void){
33 major = register_chrdev(0, "first_drv", &first_drv_fops);
34
35 first_drv_class = class_create(THIS_MODULE, "first_drv");
36
37 first_drv_class_dev = class_device_create(first_drv_class, NULL, MKDEV(major,0), NULL, "xyz");
38
39 return 0;
40 }
41
42 static void __exit first_drv_exit(void){
43 unregister_chrdev(major, "first_drv");
44
45 class_device_unregister(first_drv_class_dev);
46 class_destroy(first_drv_class);
47 }
50
51 module_init(first_drv_init);
52 module_exit(first_drv_exit);
53
54 MODULE_LICENSE("GPL");
*****************************************************************************
其中修改的信息用阴影标注了。
主要是在出入口函数中定义了一个类,这个类中有一个或多个成员,为这几个成员分配主次设备号,名称等东西。其中用到了MKDEV()这个函数:
在linux内核中,dev_t类型用来保存主设备号和词设备号,
#include
MAJOR(dev_t, dev); /*获得主设备号*/
MINOR(dev_t, dev); /*获得次设备号*/
相反,若要将主设备号和次设备号转换为dev_t类型,可以使用以下宏:
MKDEV(int major, int minor);
测试程序中,只需把第9行“/dev/xxx”改成“/dev/xyz”即可实现功能。
==================================================================
第三步:点亮熄灭LED
12 static struct class *first_drv_class;
13 static struct class_device *first_drv_class_dev;
14
15 volatile unsigned long *gpfcon = NULL;
16 volatile unsigned long *gpfdat = NULL;
17
18 static int first_drv_open(struct inode *inode, struct file *file){
19 //printk("first_drv_open!\n");
20 *gpfcon &= ~((0x3 << (4*2)) | (0x3 << (5*2)) | (0x3 << (6*2)));
21 *gpfcon |= ((0x1 << (4*2)) | (0x1 << (5*2)) | (0x1 << (6*2)));
22 return 0;
23 }
24
25 static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){
26 //printk("first_drv_write\n");
27
28 int val;
29 copy_from_user(&val, buf, count);
30
31 if(val == 1)
32 {
33 *gpfdat &= ~ ((1 << 4) | (1 << 5) | (1 << 6));
34 }
35 else
36 {
37 *gpfdat |= ((1 << 4) | (1 << 5) | (1 << 6));
38 }
39 return 0;
40 }
41
42 static struct file_operations first_drv_fops = {
43 .owner = THIS_MODULE,
44 .open = first_drv_open,
45 .write = first_drv_write,
46 };
47
48 int major;
49 static int __init first_drv_init(void){
50 major = register_chrdev(0, "first_drv", &first_drv_fops);
51
52 first_drv_class = class_create(THIS_MODULE, "first_drv");
53
54 first_drv_class_dev = class_device_create(first_drv_class, NULL, MKDEV(major,0), NULL, "xyz");
55
56 gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
57 gpfdat = gpfcon + 1;
58
59 return 0;
60 }
61
62 static void __exit first_drv_exit(void){
63 unregister_chrdev(major, "first_drv");
64
65 class_device_unregister(first_drv_class_dev);
66 class_destroy(first_drv_class);
67 iounmap(gpfcon);
68 }
69
70
71
72 module_init(first_drv_init);
73 module_exit(first_drv_exit);
74
75 MODULE_LICENSE("GPL");
其中测试程序如下:
6 int main (int argc, char **argv){
7 int fd;
8 int val = 1;
9 fd = open("/dev/xyz",O_RDWR);
10 if (fd < 0)
11 printf("cannot open!\n");
12
13 if (argc != 2){
14 printf("Usage:\n");
15 printf("%s
16 return 0;
17 }
18
19 if (strcmp(argv[1],"on") == 0)
20 {
21 val = 1;
22 }
23 else{
24 val = 0;
25 }
26
27 write (fd, &val, 4);
28 return 0;
29 }
出现错误如下:
(1)指针的用法:
在映射的时候,第56 57行的是*gpfcon=(volatile unsigned long *)ioremap(0x56000050, 16);和*gpfdat = *gpfcon + 1;还是gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1;这两者有什么区别??
(2)很粗心的写了一个copy_from_usr,正确的是copy_from_user,在linux中的用户目录为usr,不
要混淆。下图为写错以后,在开发板上面的提示信息。
(3)下图为安装驱动的时候,提示的错误:仔细看这些提示,第一行提示为无法挂载内核NULL点在虚拟内存上面,第七行提示为:PC is at first_drv_init+0x7c/6xc8 [first_drv];可以分析出来是是 first_drv_init里面出错了。
下图是我去查看入口函数的时候截的图,暂时还是不太清楚到底是为什么!!
(4)当感觉没错误的时候,执行却没有现象,后来查看firstdrvtest代码的时候,发现strcmp函数的使用,如果两者相同,则函数值为0,不同的话为1。
注意:在单片机编程的过程中,直接使用物理地址就可以访问寄存器,但是在linux和驱动程序中,需要使用虚拟地址来操作寄存器,这就用到了ioremap函数,将物理地址映射成虚拟地址。