了解两个数据结构体:struct inode和struct file
a)
struct inode {
dev_t i_rdev;//保存设备号信息
struct cdev *i_cdev; //指向字符设备对象,例如:led_cdev
...
};
功能:描述一个文件的物理上的信息,例如:ls -l /dev/myled //看到的各种属性都是用inode来描述
生命周期:一旦创建文件成功(mknod,vim,touch,dd,cp,mv等),linux内核就会帮你自动创建一个inode对象
用此对象来描述创建的新文件各种属性信息,一旦删除文件(rm),内核也会帮你自动删除对应的inode对象
结论:
1.一个文件仅有一个inode对象
2.字符设备驱动操作接口
struct file_operations {
int (*open)(struct inode *inode, ....);
int (*release)(struct inode *inode, ....);
};
这些接口的第一个形参inode指针就是指向内核自动帮你创建的inode对象
将来驱动open,release接口可以通过inode指针来获取对应的文件信息
重点关注设备号:i_rdev这个成员
能够提取主次设备号:MAJOR(inode->i_rdev), MINOR(inode->i_rdev);
b)
struct file {
const struct file_operations *f_op;//指向字符设备驱动硬件操作接口对象,例如:led_fops
...
};
功能:描述一个文件被成功打开open以后的各种属性(权限啦,读写位置信息等)
生命周期:文件一旦被open成功打开,linux内核(sys_open)自动帮你创建一个file对象
来描述文件打开以后的属性,文件一旦被关闭close,内核自动销毁对应的file对象
结论:
1.一个文件可以有多个file对象
2.字符设备驱动操作接口:
struct file_operations {
int (*open)(struct inode *inode, struct file *file);
int (*release)(struct inode *inode, struct file *file);
int (*read)(struct file *file,...);
int (*write)(struct file *file, ...);
int (*unlocked_ioctl)(struct file *file, ...);
};
这些接口的file指针指向内核创建的file对象
问:如何通过file指针来获取对应文件的inode对象指针呢?
答:参见fbmem.c(LCD显示屏驱动)
struct inode *inode = file->f_path.dentry->d_inode;
再通过inode就可以获取设备号了!
案例:编写LED字符设备驱动,利用ioctl实现开关某个灯
目前要求:将四个LED灯作为四个硬件个体,又由于他们的硬件特性是一致的
所以他们共用一个驱动程序,驱动将来通过次设备号来区分LED硬件个体
分析:
硬件个数:4个
设备文件:4个:/dev/myled0,/dev/myled1,/dev/myled2,/dev/myled3
驱动程序:1个
cdev对象:1个
主设备号:1个
次设备号:4个:0/1/2/3
操作接口对象led_fops:1个,共用的,所以led_ioctl接口函数也是共用
inode对象:4个,inode0,inode1,inode2,inode3
file对象:4个,file0,file1,file2,file3
fd:4个,fd0,fd1,fd2,fd3
关系:
/dev/myled0->fd0->file0->inode0->i_rdev->主A,次0
/dev/myled1->fd1->file1->inode1->i_rdev->主A,次1
/dev/myled2->fd2->file2->inode2->i_rdev->主A,次2
/dev/myled3->fd3->file3->inode3->i_rdev->主A,次3
应用程序:
int fd0 = open("/dev/myled0");
ioctl(fd0, ...);
1 //应用测试程序
2 #include<stdio.h>
3 #include<sys/types.h>
4 #include<sys/stat.h>
5 #include<fcntl.h>
6
7 #define LED_ON 0x100001 //开灯命令
8 #define LED_OFF 0x100002 //关灯命令
9
10 int main(int argc,char *argv[]){
11 int fd0, fd1, fd2, fd3;
12
13
14 fd0 = open("/dev/myled0", O_RDWR);
15 if(fd0 < 0){
16 printf("设备打开失败.\n");
17 return -1;
18 }
19 fd1 = open("/dev/myled1", O_RDWR);
20 if(fd1 < 0){
21 printf("设备打开失败.\n");
22 return -1;
23 }
24 fd2 = open("/dev/myled2", O_RDWR);
25 if(fd2 < 0){
26 printf("设备打开失败.\n");
27 return -1;
28 }
29 fd3 = open("/dev/myled3", O_RDWR);
30 if(fd3 < 0){
31 printf("设备打开失败.\n");
32 return -1;
33 }
34
35
36 ioctl(fd0, LED_ON);
37 sleep(1);
38 ioctl(fd0, LED_OFF);
39 sleep(1);
40 ioctl(fd1, LED_ON);
41 sleep(1);
42 ioctl(fd1, LED_OFF);
43 sleep(1);
44 ioctl(fd2, LED_ON);
45 sleep(1);
46 ioctl(fd2, LED_OFF);
47 sleep(1);
48 ioctl(fd3, LED_ON);
49 sleep(1);
50 ioctl(fd3, LED_OFF);
51 sleep(1);
52
53 close(fd0);
54 close(fd1);
55 close(fd2);
56 close(fd3);
57 return 0;
58 }
驱动:
led_ioctl(struct file *file, ...) {
//file->file0
struct inode *inode = file->f_path.dentry->d_inode; //inode0
int minor = MINOR(inode->i_rdev); //获取次设备号0
//通过次设备号区分硬件个体
gpio_set_value(led_info[minor].gpio, 0);
}
应用程序:
int fd1 = open("/dev/myled1");
ioctl(fd1, ...);
驱动:
led_ioctl(struct file *file, ...) {
//file->file1
struct inode *inode = file->f_path.dentry->d_inode; //inode1
int minor = MINOR(inode->i_rdev); //获取次设备号1
//通过次设备号区分硬件个体
gpio_set_value(led_info[minor].gpio);
}
1 //驱动程序
2 #include<linux/init.h>
3 #include<linux/module.h>
4 #include<linux/gpio.h>
5 #include<mach/platform.h>
6 #include<linux/fs.h>//struct file_operation声明
7 #include<linux/cdev.h>//struct cdev声明
8 #include<linux/uaccess.h>//copy_from_user声明
9 #include<linux/device.h>//struct class/class_create等声明
10
11 //声明描述LED硬件信息的结构体
12 struct led_resource{
13 char* name;
14 int gpio;
15 };
16 //结构体类型数组
17 static struct led_resource led_info[]={
18 {
19 .name = "LED1",
20 .gpio = PAD_GPIO_C + 12
21 },
22 {
23 .name = "LED2",
24 .gpio = PAD_GPIO_C + 7
25 },
26 {
27 .name = "LED3",
28 .gpio = PAD_GPIO_C + 11
29 },
30 {
31 .name = "LED4",
32 .gpio = PAD_GPIO_B + 26
33 }
34 };
35 //定义设备号变量
36 static dev_t dev;
37
38 //定义硬件控制接口操作函数
39 //调用关系:应用ioctl->c库ioctl->软中断->sys_ioctl->驱动led_ioctl
40 //ioctl(fdx, LED_ON/LED_OFF)
41 //fd0->file0->inode0->i_rdev->次设备号0
42 //fd1->file1->inode1->i_rdev->次设备号1
43 //fd0->file2->inode2->i_rdev->次设备号2
44 //fd0->file3->inode3->i_rdev->次设备号3
45 #define LED_ON 0x100001
46 #define LED_OFF 0x100002
47 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long buf){
48 //1.通过file获得对应的inode指针
49 struct inode *inode = file->f_path.dentry->d_inode;
50 //2.通过inode获取次设备号
51 int minor = MINOR(inode->i_rdev);
52 //结果:minor=0/1/2/3
53 //3.解析命令cmd
54 switch(cmd) {
55 case LED_ON:
56 gpio_set_value(led_info[minor].gpio, 0);
57 printk("%s:打开第%d个灯\n", __func__, minor+1);
58 break;
59 case LED_OFF:
60 gpio_set_value(led_info[minor].gpio, 1);
61 printk("%s:关闭第%d个灯\n", __func__, minor+1);
62 break;
63 default:
64 printk("命令不对.\n");
65 return -1;
66 }
67 return 0;
68 }
69
70 //定义初始化硬件操作接口对象
71 static struct file_operations led_fops = {
72 .unlocked_ioctl = led_ioctl //控制接口
73 };
74 //定义字符设备驱动对象
75 static struct cdev led_cdev;
76
77 //定义设备类对象指针(幼苗)
78 static struct class *cls;
79
80 static int led_init(void){
81 //1,申请GPIO资源
82 //2,配置为输出,默认输出1,关灯
83 int i;
84 for(i = 0; i < ARRAY_SIZE(led_info); i++){
85 gpio_request(led_info[i].gpio,led_info[i].name);
86 gpio_direction_output(led_info[i].gpio,1);
87 }
88 //3,申请设备号
89 alloc_chrdev_region(&dev, 0, 4, "tarena");//次设备号:0/1/2/3
90 //4,初始化字符设备驱动对象,关联硬件操作接口对象
91 cdev_init(&led_cdev, &led_fops);
92 //5,向内核注册设备字符驱动对象,这样内核就有了一个真实的LED驱动并且提供了open,release接口
93 //静静的在内容中等待应用程序来访问
94 cdev_add(&led_cdev, dev, 4);
95 //6.创建设备类对象(树枝)
96 //结果是在:/sys/class/tarena1目录
97 cls = class_create(THIS_MODULE, "tarena1");
98 //7.创建设备文件:/dev/myled(苹果)
99 device_create(cls, NULL, MKDEV(MAJOR(dev), 0), NULL, "myled0");
100 device_create(cls, NULL, MKDEV(MAJOR(dev), 1), NULL, "myled1");
101 device_create(cls, NULL, MKDEV(MAJOR(dev), 2), NULL, "myled2");
102 device_create(cls, NULL, MKDEV(MAJOR(dev), 3), NULL, "myled3");
103 return 0;
104 }
105
106 static void led_exit(void){
107 int i;
108 //1.从内核卸载字符设备
109 cdev_del(&led_cdev);
110 //2.释放设备号
111 unregister_chrdev_region(dev, 4);
112 //3.关灯
113 //4.释放GPIO资源
114 for(i=0;i<ARRAY_SIZE(led_info);i++){
115 gpio_set_value(led_info[i].gpio, 1);
116 gpio_free(led_info[i].gpio);
117 }
118 //5.删除设备文件,摘苹果
119 device_destroy(cls, MKDEV(MAJOR(dev), 0));
120 device_destroy(cls, MKDEV(MAJOR(dev), 1));
121 device_destroy(cls, MKDEV(MAJOR(dev), 2));
122 device_destroy(cls, MKDEV(MAJOR(dev), 3));
123 //6.删除设备类对象,砍树枝
124 class_destroy(cls);
125 }
126 module_init(led_init);
127 module_exit(led_exit);
128 MODULE_LICENSE("GPL");