码迷,mamicode.com
首页 > 系统相关 > 详细

linux驱动初探之字符驱动

时间:2015-08-28 15:08:57      阅读:267      评论:0      收藏:0      [点我收藏+]

标签:

关键字:字符驱动、动态生成设备节点、helloworld

 

linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~

 

  此驱动程序基于linux3.0的内核,exynos4412开发板。

 

首先,打开平台文件,此开发板的平台文件是arch\arm\mach-exynos\mach-itop4412.c,不同平台位置是不一样的。

  申明一下设备信息,这里以编译进kernel为例

1 #ifdef CONFIG_HELLO_CHAR_CTL
2 struct platform_device s3c_device_hello_char_ctl = {
3         .name   = "Hello_Jni_Char",
4         .id             = -1,
5 };
6 #endif

 

然后在struct platform_device *smdk4x12_devices[]数组里注册设备信息

1 #ifdef CONFIG_HELLO_CHAR_CTL
2     &s3c_device_hello_char_ctl,
3 #endif

至此,平台文件配置完毕。

 

接下来,开始大餐,驱动程序的编写!

 

注意:在linux的每一个驱动程序里面都需要加上如下信息,表明开源

1 MODULE_LICENSE("Dual BSD/GPL");
2 MODULE_AUTHOR("pngcui");

 

每一个驱动程序都是从init函数开始的,同时也需要编写exit函数,在初始化函数中需要生成主设备号与从设备号,因为linux是根据主从设备号来寻找相应的硬件的

 1 #define DEV_MAJOR 0
 2 #define DEV_MINOR 0
 3 
 4 int numdev_major = DEV_MAJOR;
 5 int numdev_minor = DEV_MINOR;
 6 
 7 
 8 /*输入主设备号*/
 9 module_param(numdev_major,int,S_IRUSR);
10 /*输入次设备号*/
11 module_param(numdev_minor,int,S_IRUSR);

进入驱动程序中的init函数中,进行注册设备号,生成设备节点DEVICE_NAME

1 #define DEVICE_NAME "chardevnode"

注意:此驱动由于控制了两个gpio引脚所以出现了gpio_init(),在这里可以忽略,对于gpio的控制,在下一篇博客中会写到。

 1 static int scdev_init(void)
 2 {
 3     int ret = 0,i;
 4     dev_t num_dev;
 5     
 6     
 7     printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);
 8     printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);
 9     
10     if(numdev_major){
11         num_dev = MKDEV(numdev_major,numdev_minor);
12         ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
13     }
14     else{
15         /*动态注册设备号*/
16         ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
17         /*获得主设备号*/
18         numdev_major = MAJOR(num_dev);
19         printk(KERN_EMERG "adev_region req %d !\n",numdev_major);
20     }
21     if(ret<0){
22         printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);        
23     }
24     myclass = class_create(THIS_MODULE,DEVICE_NAME);
25     
26     
27     my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
28     if(!my_devices){
29         ret = -ENOMEM;
30         goto fail;
31     }
32     memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
33     
34     /*设备初始化*/
35     for(i=0;i<DEVICE_MINOR_NUM;i++){
36         my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
37         memset(my_devices[i].data,0,REGDEV_SIZE);
38         /*设备注册到系统*/
39         reg_init_cdev(&my_devices[i],i);
40         
41         /*创建设备节点*/
42         device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
43     }
44     
45     ret = gpio_init();
46     if(ret){
47         printk(KERN_EMERG "gpio_init failed!\n");
48     }    
49         
50     printk(KERN_EMERG "scdev_init!\n");
51     return 0;
52 
53 fail:
54     /*注销设备号*/
55     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
56     printk(KERN_EMERG "kmalloc is fail!\n");
57     
58     return ret;
59 }
60 
61 static void scdev_exit(void)
62 {
63     int i;
64     printk(KERN_EMERG "scdev_exit!\n");
65     
66     /*除去字符设备*/
67     for(i=0;i<DEVICE_MINOR_NUM;i++){
68         cdev_del(&(my_devices[i].cdev));
69         /*摧毁设备节点函数*/
70         device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
71     }
72     /*释放设备class*/
73     class_destroy(myclass);
74     /*释放内存*/
75     kfree(my_devices);
76     
77     /*释放GPIO*/
78     for(i=0;i<LED_NUM;i++){
79         gpio_free(led_gpios[i]);
80     }
81         
82     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
83 }
84 
85 
86 module_init(scdev_init);
87 /*初始化函数*/
88 module_exit(scdev_exit);
89 /*卸载函数*/

然后把设备注册到系统

 1 static void reg_init_cdev(struct reg_dev *dev,int index){
 2     int err;
 3     int devno = MKDEV(numdev_major,numdev_minor+index);
 4 
 5     /*数据初始化*/
 6     cdev_init(&dev->cdev,&my_fops);
 7     dev->cdev.owner = THIS_MODULE;
 8     dev->cdev.ops = &my_fops;
 9     
10     /*注册到系统*/
11     err = cdev_add(&dev->cdev,devno,1);
12     if(err){
13         printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);
14     }
15     else{
16         printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);
17     }
18 }
dev->cdev.ops = &my_fops;这一句就指明了此驱动程序的函数接口
1 struct file_operations my_fops = {
2     .owner = THIS_MODULE,
3     .open = chardevnode_open,
4     .release = chardevnode_release,
5     .unlocked_ioctl = chardevnode_ioctl,
6     .read = chardevnode_read,
7     .write = chardevnode_write,
8     .llseek = chardevnode_llseek,
9 };

在这里的所有函数接口必须都实现,否则会编译报错。

其中最重要的两个函数是open、ioctl这两个函数,open函数用来打开设备节点,然后才可以对设备进行驱动。

然后是ioctl函数,一般上层应用都是通过调用此函数来对设备进行操作的。

 1 /*打开操作*/
 2 static int chardevnode_open(struct inode *inode, struct file *file){
 3     printk(KERN_EMERG "chardevnode_open is success!\n");
 4     
 5     return 0;
 6 }
 7 /*关闭操作*/
 8 static int chardevnode_release(struct inode *inode, struct file *file){
 9     printk(KERN_EMERG "chardevnode_release is success!\n");
10     
11     return 0;
12 }
13 /*IO操作*/
14 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
15     
16     switch(cmd)
17     {
18         case 0:
19         case 1:
20             if (arg > LED_NUM) {
21                 return -EINVAL;
22             }
23 
24             gpio_set_value(led_gpios[arg], cmd);
25             break;
26 
27         default:
28             return -EINVAL;
29     }
30     
31     printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);
32     
33     return 0;
34 }
35 
36 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
37     return 0;
38 }
39 
40 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
41     return 0;
42 }
43 
44 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){
45     return 0;
46 }

到这里一个完整的字符驱动就编写完毕了。

 

最后附上完整的驱动程序

技术分享
  1 /*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
  2 #include <linux/init.h>
  3 /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
  4 #include <linux/module.h>
  5 /*定义module_param module_param_array的头文件*/
  6 #include <linux/moduleparam.h>
  7 /*定义module_param module_param_array中perm的头文件*/
  8 #include <linux/stat.h>
  9 /*三个字符设备函数*/
 10 #include <linux/fs.h>
 11 /*MKDEV转换设备号数据类型的宏定义*/
 12 #include <linux/kdev_t.h>
 13 /*定义字符设备的结构体*/
 14 #include <linux/cdev.h>
 15 /*分配内存空间函数头文件*/
 16 #include <linux/slab.h>
 17 /*包含函数device_create 结构体class等头文件*/
 18 #include <linux/device.h>
 19 
 20 /*自定义头文件*/
 21 #include "char_driver_leds.h"
 22 
 23 /*Linux中申请GPIO的头文件*/
 24 #include <linux/gpio.h>
 25 /*三星平台的GPIO配置函数头文件*/
 26 /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
 27 #include <plat/gpio-cfg.h>
 28 /*三星平台4412平台,GPIO宏定义头文件*/
 29 #include <mach/gpio-exynos4.h>
 30 
 31 
 32 MODULE_LICENSE("Dual BSD/GPL");
 33 /*声明是开源的,没有内核版本限制*/
 34 MODULE_AUTHOR("iTOPEET_dz");
 35 /*声明作者*/
 36 
 37 static int led_gpios[] = {
 38     EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
 39 };
 40 #define LED_NUM        ARRAY_SIZE(led_gpios)
 41 
 42 #define DEVICE_NAME "chardevnode"
 43 
 44 #define DEVICE_MINOR_NUM 1
 45 
 46 #define DEV_MAJOR 0
 47 #define DEV_MINOR 0
 48 #define REGDEV_SIZE 3000
 49 
 50 
 51 struct reg_dev
 52 {
 53     char *data;
 54     unsigned long size;
 55     
 56     struct cdev cdev;
 57 };
 58 
 59 
 60 int numdev_major = DEV_MAJOR;
 61 int numdev_minor = DEV_MINOR;
 62 
 63 /*输入主设备号*/
 64 module_param(numdev_major,int,S_IRUSR);
 65 /*输入次设备号*/
 66 module_param(numdev_minor,int,S_IRUSR);
 67 
 68 static struct class *myclass;
 69 struct reg_dev *my_devices;
 70 
 71 /*打开操作*/
 72 static int chardevnode_open(struct inode *inode, struct file *file){
 73     printk(KERN_EMERG "chardevnode_open is success!\n");
 74     
 75     return 0;
 76 }
 77 /*关闭操作*/
 78 static int chardevnode_release(struct inode *inode, struct file *file){
 79     printk(KERN_EMERG "chardevnode_release is success!\n");
 80     
 81     return 0;
 82 }
 83 /*IO操作*/
 84 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
 85     
 86     switch(cmd)
 87     {
 88         case 0:
 89         case 1:
 90             if (arg > LED_NUM) {
 91                 return -EINVAL;
 92             }
 93 
 94             gpio_set_value(led_gpios[arg], cmd);
 95             break;
 96 
 97         default:
 98             return -EINVAL;
 99     }
100     
101     printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);
102     
103     return 0;
104 }
105 
106 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
107     return 0;
108 }
109 
110 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
111     return 0;
112 }
113 
114 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){
115     return 0;
116 }
117 struct file_operations my_fops = {
118     .owner = THIS_MODULE,
119     .open = chardevnode_open,
120     .release = chardevnode_release,
121     .unlocked_ioctl = chardevnode_ioctl,
122     .read = chardevnode_read,
123     .write = chardevnode_write,
124     .llseek = chardevnode_llseek,
125 };
126 
127 
128 /*设备注册到系统*/
129 static void reg_init_cdev(struct reg_dev *dev,int index){
130     int err;
131     int devno = MKDEV(numdev_major,numdev_minor+index);
132 
133     /*数据初始化*/
134     cdev_init(&dev->cdev,&my_fops);
135     dev->cdev.owner = THIS_MODULE;
136     dev->cdev.ops = &my_fops;
137     
138     /*注册到系统*/
139     err = cdev_add(&dev->cdev,devno,1);
140     if(err){
141         printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);
142     }
143     else{
144         printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);
145     }
146 }
147 
148 static int gpio_init(void){
149     int i=0,ret;
150     
151     for(i=0;i<LED_NUM;i++){
152         ret = gpio_request(led_gpios[i], "LED");
153         if (ret) {
154             printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,i,ret);
155             return -1;
156         }
157         else{
158             s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
159             gpio_set_value(led_gpios[i], 1);            
160         }
161     }
162     
163     return 0;
164 }
165 
166 
167 static int scdev_init(void)
168 {
169     int ret = 0,i;
170     dev_t num_dev;
171     
172     
173     printk(KERN_EMERG "numdev_major is %d!\n",numdev_major);
174     printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor);
175     
176     if(numdev_major){
177         num_dev = MKDEV(numdev_major,numdev_minor);
178         ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
179     }
180     else{
181         /*动态注册设备号*/
182         ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
183         /*获得主设备号*/
184         numdev_major = MAJOR(num_dev);
185         printk(KERN_EMERG "adev_region req %d !\n",numdev_major);
186     }
187     if(ret<0){
188         printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);        
189     }
190     myclass = class_create(THIS_MODULE,DEVICE_NAME);
191     
192     
193     my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
194     if(!my_devices){
195         ret = -ENOMEM;
196         goto fail;
197     }
198     memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
199     
200     /*设备初始化*/
201     for(i=0;i<DEVICE_MINOR_NUM;i++){
202         my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
203         memset(my_devices[i].data,0,REGDEV_SIZE);
204         /*设备注册到系统*/
205         reg_init_cdev(&my_devices[i],i);
206         
207         /*创建设备节点*/
208         device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
209     }
210     
211     return 0;
212 
213 fail:
214     /*注销设备号*/
215     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
216     printk(KERN_EMERG "kmalloc is fail!\n");
217     
218     return ret;
219 }
220 
221 static void scdev_exit(void)
222 {
223     int i;
224     printk(KERN_EMERG "scdev_exit!\n");
225     
226     /*除去字符设备*/
227     for(i=0;i<DEVICE_MINOR_NUM;i++){
228         cdev_del(&(my_devices[i].cdev));
229         /*摧毁设备节点函数d*/
230         device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
231     }
232     /*释放设备class*/
233     class_destroy(myclass);
234     /*释放内存*/
235     kfree(my_devices);
236     
237     /*释放GPIO*/
238     for(i=0;i<LED_NUM;i++){
239         gpio_free(led_gpios[i]);
240     }
241         
242     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
243 }
244 
245 
246 module_init(scdev_init);
247 /*初始化函数*/
248 module_exit(scdev_exit);
249 /*卸载函数*/
View Code

 

以及完整的测试程序

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <sys/ioctl.h>
 6 #include <stdio.h>
 7 
 8 int main(){
 9     
10     int fd;
11     
12     if((fd = open("/dev/chardevnode0",O_RDWR|O_NDELAY)) < 0)
13         printf("Hello_Jni open failed!\n");
14     
15     ioctl(fd,0,0);
16     ioctl(fd,0,1);
17     
18     close(fd);
19     
20     return 0;
21 }

 

linux驱动初探之字符驱动

标签:

原文地址:http://www.cnblogs.com/pngcui/p/4766504.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!