字符設備驅動 —— 字符設備驅動框架


目錄·:

  1、概述   

  2、設備號、設備節點 

  3、字符設備驅動框架

  4、申請設備號

  5、創建設備節點

  6、實現文件IO接口--fops

  7、應用程序控制驅動

  8、驅動程序控制外設

 

  1、概述

  linux中一切皆文件,設備也如此,並且以操作文件即文件IO的方式訪問設備。

  應用程序只能通過庫函數中的系統調用來操作硬件,對於每個系統調用,驅動程序中都會有一個與之對應的函數,對於字符設備驅動,這些函數集中在file_operations結構體中。當應用程序使用系統調用read、write等函數訪問設備時,最終會調用到file_opeartions中的成員,當然一開始fops中的只是一大堆函數指針的調用接口,具體的函數就需要我們在驅動中實現,實現對應操作函數后,與fops一對接,應用程序最終就能對硬件進行控制了。

  那么問題來了,當應用程序使用系統調用訪問設備時,linux系統怎么知道調用哪一個驅動的fops中的成員呢?

 2、設備號、設備節點

      設備號和驅動相關聯

    設備號是一個ID,設備節點就是驅動文件

    字符設備和塊設備是獨立的,雖然設備號可能相同,但卻是不同的設備

  

 

 

   3、字符設備驅動框架

作為字符設備驅動要素:
    1,必須有一個設備號,用在眾多到設備驅動中進行區分
    2,用戶必須知道設備驅動對應到設備節點(設備文件)
        linux把所有到設備都看成文件

        crw-r----- 1 root root 13, 64 Mar 28 20:14 event0
        crw-r----- 1 root root 13, 65 Mar 28 20:14 event1
        crw-r----- 1 root root 13, 66 Mar 28 20:14 event2
    3,對設備操作其實就是對文件操作,應用空間操作open,read,write的時候
        實際在驅動代碼有對應到open, read,write

  

  4、申請設備號

// 1、注冊獲取設備號
// 2、初始化設備
// 3、操作設備 file_operations – open release read write ioctl…
// 4、兩個宏定義 module_init module_exit 
// 5、注冊設備號 register_chrdev_region
// 6、cdev_init 初始化字符設備
// 7、cdev_add 添加字符設備到系統

  1)向系統申請主設備號

int register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)

//參數:
//1、major:主設備號
//     設備號(32bit–dev_t)==主設備號(高12bit) + 次設備號(低20bit)
//      主設備號:表示一類設備—(如:camera)
//      次設備號: 表示一類設備中某一個—(如:前置camera/后置camera)
//       0 -->動態分配  ; 250 --> 給定整數,靜態指定
//2、name: 描述設備信息,可自定義
//        在目錄/proc/devices列舉出了所有的已經注冊的設備
//3、fops: 文件操作對象
//         提供open, read,write
//返回值:成功-0,失敗-負數

 

  2)釋放設備號

void unregister_chrdev(unsigned int major, const char * name)

  

  3)例:主設備號的申請

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/fs.h>
 4 
 5 //靜態指定
 6 static int dev_major = 250;
 7 
 8 const struct file_operations my_fops = {};
 9 
10 static int __init chr_drv_init(void)
11 {
12     //申請設備號
13     int ret;
14     ret = register_chrdev(dev_major, "chr_dev", &my_fops);
15     if(ret == 0)
16     {
17         printk("register success!\n");
18     }
19     else
20     {
21         printk("register failed!\n");
22         return -EFAULT;
23     }
24     return 0;
25 }
26 
27 static void __exit chr_drv_exit(void)
28 {
29     //釋放設備號資源
30     unregister_chrdev(dev_major, "chr_dev");
31 }
32 
33 module_init(chr_drv_init);
34 module_exit(chr_drv_exit);
35 
36 MODULE_LICENSE("GPL");
chr_drv.c

加載驅動前:

 

 

 加載驅動后:

 

 

5、創建設備節點

  1)手動創建

··  缺點/dev/目錄中文件都是在內存中,斷電后/dev/文件就會消失

    mknod /dev/設備名  類型  主設備號 次設備號
    (主設備號要和驅動中申請的主設備號保持一致)
    比如:
        mknod  /dev/chr0  c  250 0
eg: [root@farsight drv_module]# ls
/dev/chr0 -l crw-r--r-- 1 0 0 250, 0 Jan 1 00:33 /dev/chr0

  2)自動創建

  通過udev/mdev機制

struct class *class_create(owner, name)//創建一個類

//參數:
//1、owner:THIS_MODULE
//2、name :字符串名字,自定義
//返回:
//        返回一個class指針

  創建一個設備文件:

//創建一個設備文件
struct device *device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt,...)

//參數:
//1、class結構體,class_create調用之后的返回值
//2、表示父親,一般直接填NULL
//3、設備號類型 dev_t
//4、私有數據,一般直接填NULL
//5/6、表示可變參數,字符串,表示設備節點名字

設備號類型:dev_t devt
        #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))     //獲取主設備號
        #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))     //獲取次設備號
        #define MKDEV(ma,mi)  (((ma) << MINORBITS) | (mi))      //生成設備號

  銷毀設備文件:

void device_destroy(devcls,  MKDEV(dev_major, 0));
//參數:
//1、class結構體,class_create調用之后到返回值
//2、設備號類型 dev_t

void class_destroy(devcls);
//參數:class結構體,class_create調用之后到返回值

  3)示例:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/fs.h>
 4 #include <linux/device.h>
 5 
 6 /*
 7 設備號給定方式有兩種:
 8         1,動態--參數1直接填0
 9         2,靜態--指定一個整數,250
10 */
11 static unsigned int dev_major = 250;
12 static unsigned int dev_minor = 0;
13 static struct class *devcls;
14 static struct device *dev;
15 
16 const struct file_operations my_fops = {
17 
18 };
19 
20 static int __init chr_dev_init(void)
21 {
22     //裝載一般都是申請設備號資源
23     //申請設備號
24     int ret;
25     
26     ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
27     if(ret == 0){
28         printk("register ok\n");
29     }
30     else{
31         printk("register failed\n");
32         return -EINVAL;
33     }
34 
35     //自動創建設備節點
36     devcls = class_create(THIS_MODULE, "chr_cls");
37     dev = device_create(devcls,NULL,MKDEV(dev_major,dev_minor),NULL,"chr2");
38     
39     return 0;
40 }
41 
42 static void __exit chr_dev_exit(void)
43 {
44      //卸載一般都是釋放資源
45 
46     //銷毀節點和類
47     device_destroy(devcls,MKDEV(dev_major,dev_minor));
48     class_destroy(devcls);
49     //釋放設備號
50      unregister_chrdev(dev_major, "chr_dev_test");
51 }
52 
53 module_init
54 (chr_dev_init);
55 module_exit(chr_dev_exit);
56 MODULE_LICENSE("GPL");
chr_drv.c

 

7、實現文件IO接口--fops

  1)驅動中實現文件io操作接口:struct file_operations

 1 struct file_operations {
 2         struct module *owner;
 3         loff_t             (*llseek) (struct file *, loff_t, int);
 4         ssize_t         (*read) (struct file *, char __user *, size_t, loff_t *);
 5         ssize_t         (*write) (struct file *, const char __user *, size_t, loff_t *);
 6         ssize_t         (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 7         ssize_t         (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 8         int             (*iterate) (struct file *, struct dir_context *);
 9         unsigned int     (*poll) (struct file *, struct poll_table_struct *);
10         long             (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
11         long             (*compat_ioctl) (struct file *, unsigned int, unsigned long);
12         int             (*mmap) (struct file *, struct vm_area_struct *);
13         int             (*open) (struct inode *, struct file *);
14         ....
16         long             (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
17         int             (*show_fdinfo)(struct seq_file *m, struct file *f);
18     }; //函數指針的集合,其實就是接口,我們寫驅動到時候需要去實現
19 
20     const struct file_operations my_fops = {
21             .open = chr_drv_open,
22             .read = chr_drv_read,
23             .write = chr_drv_write,
24             .release = chr_drv_close,
25     };

示例:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/fs.h>
 4 #include <linux/device.h>
 5 
 6 /*
 7 設備號給定方式有兩種:
 8         1,動態--參數1直接填0
 9         2,靜態--指定一個整數,250
10 */
11 static unsigned int dev_major = 250;
12 static unsigned int dev_minor = 0;
13 static struct class *devcls;
14 static struct device *dev;
15 
16 ssize_t chr_drv_write (struct file *filp, char __user *buf, size_t count, loff_t *fpos)
17 {
18     printk("---------write %s-------------\n",__FUNCTION__);
19     
20     return 0;
21 }
22 
23 ssize_t chr_drv_read (struct file *filp, const char __user *buf, size_t count, loff_t *fops)
24 {
25     printk("---------read %s-------------\n",__FUNCTION__);
26     
27     return 0;
28 }
29 
30 int chr_drv_open (struct inode *inode, struct file *filp)
31 {
32     printk("---------open %s-------------\n",__FUNCTION__);
33     
34     return 0;
35 }
36 
37 int chr_drv_close (struct inode *inode, struct file *filp)
38 {
39     printk("---------close %s-------------\n",__FUNCTION__);
40     
41     return 0;
42 }
43 
44 
45 
46 const struct file_operations my_fops = {
47     .open    = chr_drv_open,
48     .read    = chr_drv_read,
49     .write   = chr_drv_write,
50     .release = chr_drv_close
51 };
52 
53 static int __init chr_dev_init(void)
54 {
55     //裝載一般都是申請設備號資源
56     //申請設備號
57     int ret;
58     
59     ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
60     if(ret == 0){
61         printk("register ok\n");
62     }
63     else{
64         printk("register failed\n");
65         return -EINVAL;
66     }
67 
68     //自動創建設備節點
69     devcls = class_create(THIS_MODULE, "chr_cls");
70     dev = device_create(devcls,NULL,MKDEV(dev_major,dev_minor),NULL,"chr2");
71     
72     return 0;
73 }
74 
75 static void __exit chr_dev_exit(void)
76 {
77      //卸載一般都是釋放資源
78 
79     //銷毀節點和類
80     device_destroy(devcls,MKDEV(dev_major,dev_minor));
81     class_destroy(devcls);
82     //釋放設備號
83      unregister_chrdev(dev_major, "chr_dev_test");
84 }
85 
86 module_init
87 (chr_dev_init);
88 module_exit(chr_dev_exit);
89 MODULE_LICENSE("GPL");
chr_drv1.c

實現了底層的fops成員函數,再實現應用程序的調用

  

  2)應用程序調用文件IO控制驅動 :open、read...

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/fs.h>
 4 #include <linux/device.h>
 5 
 6 /*
 7 設備號給定方式有兩種:
 8         1,動態--參數1直接填0
 9         2,靜態--指定一個整數,250
10 */
11 static unsigned int dev_major = 250;
12 static unsigned int dev_minor = 0;
13 static struct class *devcls;
14 static struct device *dev;
15 
16 ssize_t chr_drv_write (struct file *filp, char __user *buf, size_t count, loff_t *fpos)
17 {
18     printk("---------write %s-------------\n",__FUNCTION__);
19     
20     return 0;
21 }
22 
23 ssize_t chr_drv_read (struct file *filp, const char __user *buf, size_t count, loff_t *fops)
24 {
25     printk("---------read %s-------------\n",__FUNCTION__);
26     
27     return 0;
28 }
29 
30 int chr_drv_open (struct inode *inode, struct file *filp)
31 {
32     printk("---------open %s-------------\n",__FUNCTION__);
33     
34     return 0;
35 }
36 
37 int chr_drv_close (struct inode *inode, struct file *filp)
38 {
39     printk("---------close %s-------------\n",__FUNCTION__);
40     
41     return 0;
42 }
43 
44 
45 
46 const struct file_operations my_fops = {
47     .open    = chr_drv_open,
48     .read    = chr_drv_read,
49     .write   = chr_drv_write,
50     .release = chr_drv_close
51 };
52 
53 static int __init chr_dev_init(void)
54 {
55     //裝載一般都是申請設備號資源
56     //申請設備號
57     int ret;
58     
59     ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
60     if(ret == 0){
61         printk("register ok\n");
62     }
63     else{
64         printk("register failed\n");
65         return -EINVAL;
66     }
67 
68     //自動創建設備節點
69     devcls = class_create(THIS_MODULE, "chr_cls");
70     dev = device_create(devcls,NULL,MKDEV(dev_major,dev_minor),NULL,"chr2");
71     
72     return 0;
73 }
74 
75 static void __exit chr_dev_exit(void)
76 {
77      //卸載一般都是釋放資源
78 
79     //銷毀節點和類
80     device_destroy(devcls,MKDEV(dev_major,dev_minor));
81     class_destroy(devcls);
82     //釋放設備號
83      unregister_chrdev(dev_major, "chr_dev_test");
84 }
85 
86 module_init
87 (chr_dev_init);
88 module_exit(chr_dev_exit);
89 MODULE_LICENSE("GPL");
chr_drv1.c
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <sys/stat.h>
 7 #include <fcntl.h>
 8 
 9 int main(int argc, char * argv [ ])
10 {
11     //調用驅動
12     int fd;
13     int value = 0;  //buf
14 
15     fd = open("/dev/chr2", O_RDWR);
16     if(fd < 0)
17     {
18         perror("open");
19         exit(1);
20     }
21 
22     read(fd, &value, 4);
23 
24     write(fd, &value, 4);
25 
26     close(fd);
27     
28     return 0;
29 }
chr_test.c
 1 ROOTFS_DIR = /home/linux/source/rootfs#根文件系統路徑
 2 
 3 APP_NAME = chr_test
 4 
 5 CROSS_COMPILE = /home/linux/toolchains/gcc-4.6.4/bin/arm-none-linux-gnueabi-
 6 CC = $(CROSS_COMPILE)gcc
 7 
 8 ifeq ($(KERNELRELEASE),)
 9 
10 KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412          #編譯過的內核源碼的路徑
11 CUR_DIR = $(shell pwd)     #當前路徑
12 
13 all:
14     make -C $(KERNEL_DIR) M=$(CUR_DIR) modules  #把當前路徑編成modules
15     $CC $(APP_NAME).c -o $(APP_NAME)
16     @#make -C 進入到內核路徑
17     @#M 指定當前路徑(模塊位置)
18 
19 clean:
20     make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
21 
22 install:
23     sudo cp -raf *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module     #把當前的所有.ko文件考到根文件系統的drv_module目錄
24 
25 else
26 
27 obj-m += chr_drv1.o    #指定內核要把哪個文件編譯成ko
28 
29 endif
Makefile

 測試結果;

 

 

7、應用程序控制驅動

應用程序要控制驅動,就涉及用戶空間與內核空間的數據交互,如何實現?通過以下函數:

  1)copy_to_user

1 //將數據從內核空間拷貝到用戶空間,一般是在驅動中chr_drv_read()用
2 int copy_to_user(void __user * to, const void * from, unsigned long n)
3 //參數:
4 //1:應用驅動中的一個buffer
5 //2:內核空間到一個buffer
6 //3:個數
7 //返回值:大於0,表示出錯,剩下多少個沒有拷貝成功等於0,表示正確

  2)copy_from_user

1 //將數據從用戶空間拷貝到內核空間,一般是在驅動中chr_drv_write()用
2 int copy_from_user(void * to, const void __user * from, unsigned long n)
3 //參數:
4 //1:內核驅動中的一個buffer
5 //2:應用空間到一個buffer
6 //3:個數

示例:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>
  4 #include <linux/device.h>
  5 #include <asm/uaccess.h>
  6 
  7 
  8 /*
  9 設備號給定方式有兩種:
 10         1,動態--參數1直接填0
 11         2,靜態--指定一個整數,250
 12 */
 13 static unsigned int dev_major = 250;
 14 static unsigned int dev_minor = 0;
 15 static struct class *devcls;
 16 static struct device *dev;
 17 
 18 static int kernal_val = 555; 
 19 
 20 // 用戶空間向內核空間傳數據
 21 ssize_t chr_drv_write (struct file *filp, char __user *buf, size_t count, loff_t *fpos)
 22 {
 23     printk("---------write %s-------------\n",__FUNCTION__);
 24     
 25     int ret;
 26     int value;  //從用戶空間獲取到的數據存在value中
 27     ret = copy_from_user(&value, buf, count);
 28     if(ret > 0)
 29     {
 30         printk("copy_from_user");
 31         return -EFAULT;
 32     }
 33 
 34     printk("__KERNEL__: value = %d\n", value);
 35     
 36     return 0;
 37 }
 38 
 39 // read(fd, buf, size)  用戶空間獲取內核空間的數據
 40 ssize_t chr_drv_read (struct file *filp, const char __user *buf, size_t count, loff_t *fops)
 41 {
 42     printk("---------read %s-------------\n",__FUNCTION__);
 43 
 44     int ret;
 45     ret = copy_to_user(buf, &kernal_val, count);
 46     if(ret > 0)
 47     {
 48         printk("copy_to_user");
 49         return -EFAULT;
 50     }
 51     
 52     return 0;
 53 }
 54 
 55 int chr_drv_open (struct inode *inode, struct file *filp)
 56 {
 57     printk("---------open %s-------------\n",__FUNCTION__);
 58     
 59     return 0;
 60 }
 61 
 62 int chr_drv_close (struct inode *inode, struct file *filp)
 63 {
 64     printk("---------close %s-------------\n",__FUNCTION__);
 65     
 66     return 0;
 67 }
 68 
 69 
 70 
 71 const struct file_operations my_fops = {
 72     .open    = chr_drv_open,
 73     .read    = chr_drv_read,
 74     .write   = chr_drv_write,
 75     .release = chr_drv_close
 76 };
 77 
 78 static int __init chr_dev_init(void)
 79 {
 80     //裝載一般都是申請設備號資源
 81     //申請設備號
 82     int ret;
 83     
 84     ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
 85     if(ret == 0){
 86         printk("register ok\n");
 87     }
 88     else{
 89         printk("register failed\n");
 90         return -EINVAL;
 91     }
 92 
 93     //自動創建設備節點
 94     devcls = class_create(THIS_MODULE, "chr_cls");
 95     dev = device_create(devcls,NULL,MKDEV(dev_major,dev_minor),NULL,"chr2");
 96     
 97     return 0;
 98 }
 99 
100 static void __exit chr_dev_exit(void)
101 {
102      //卸載一般都是釋放資源
103 
104     //銷毀節點和類
105     device_destroy(devcls,MKDEV(dev_major,dev_minor));
106     class_destroy(devcls);
107     //釋放設備號
108      unregister_chrdev(dev_major, "chr_dev_test");
109 }
110 
111 module_init
112 (chr_dev_init);
113 module_exit(chr_dev_exit);
114 MODULE_LICENSE("GPL");
chr_drv1.c
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <sys/stat.h>
 7 #include <fcntl.h>
 8 
 9 
10 int main(int argc, char * argv [ ])
11 {
12     //調用驅動
13     int fd;
14     int value = 0;  //buf
15 
16     fd = open("/dev/chr2", O_RDWR);
17     if(fd < 0)
18     {
19         perror("open");
20         exit(1);
21     }
22 
23     read(fd, &value, 4);   //應用程序從內核獲取4字節數據存放在value中
24     printf("__USER__: value = %d\n", value);  //打印獲取的值
25     sleep(1);  //sleep1秒,防止串口打印信息不完整
26 
27     //重新給value賦值,傳給內核
28     value = 666;
29     write(fd, &value, 4);
30     
31 
32     close(fd);
33     
34     return 0;
35 }
chr_test.c

測試:

 

 

8、驅動程序控制外設

  之前我們了解了應用程序如何與內核空間進行數據交互,那么內核驅動與外設間的控制是怎么樣的?

  寫過裸機程序的都知道,可以通過修改外設對應的控制寄存器來控制外設,即向寄存器的地址寫入數據,這個地址就是物理地址,且物理地址是已知的,有硬件設計決定。

  在內核中,同樣也是操作地址控制外設,但是內核中的地址,是經過MMU映射后的虛擬地址,而且CPU通常並沒有為這些已知的外設I/O內存資源的物理地址預定義虛擬地址范圍,驅動程序並不能直接通過物理地址訪問I/O內存資源,而必須將它們映射到核心虛地址空間內,然后才能根據映射所得到的核心虛地址范圍,通過訪內指令訪問這些I/O內存資源。Linux在io.h頭文件中聲明了函數ioremap(),用來將I/O內存資源的物理地址映射到核心虛地址空間中

  ioremap的函數如下:

1 //映射虛擬地址
2 void *ioremap(cookie, size)
3 //參數:
4 //1、cookie:物理地址
5 //2、size:長度(連續映射一定長度的地址空間)
6 //返回值:虛擬地址

  解除映射:

1 //去映射--解除映射
2 void iounmap(void __iomem *addr)
3 //參數:映射后的虛擬地址

  實例:通過驅動控制LED燈

  LED —— GPX2_7 —— GPX2CON —— 0x11000C40

               GPX2DAT——   0x11000C44

 

     將0x11000c40映射為虛擬地址

  

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>
  4 #include <linux/device.h>
  5 #include <asm/uaccess.h>
  6 #include <asm/io.h>
  7 
  8 //物理地址
  9 #define GPX2CON 0x11000C40
 10 #define GPX2_SIZE 8
 11 
 12 //存放虛擬地址的指針
 13 volatile unsigned long *gpx2conf;
 14 volatile unsigned long *gpx2dat;
 15 
 16 static unsigned int dev_major = 250;
 17 static unsigned int dev_minor = 0;
 18 static struct class *devcls;
 19 static struct device *dev;
 20 
 21 static int kernal_val = 555; 
 22 
 23 // 用戶空間向內核空間傳數據
 24 ssize_t chr_drv_write (struct file *filp, char __user *buf, size_t count, loff_t *fpos)
 25 {
 26         
 27     int ret;
 28     int value;  //從用戶空間獲取到的數據存在value中
 29     ret = copy_from_user(&value, buf, count);
 30     if(ret > 0)
 31     {
 32         printk("copy_from_user");
 33         return -EFAULT;
 34     }
 35 
 36     if(value)  //根據用戶傳遞的值決定燈的亮滅
 37     {
 38         *gpx2dat |=  (0x1<<7);
 39     }else
 40         {
 41         *gpx2dat &= ~(0x1<<7);
 42     }
 43     
 44     return 0;
 45 }
 46 
 47 // read(fd, buf, size)  用戶空間獲取內核空間的數據
 48 ssize_t chr_drv_read (struct file *filp, const char __user *buf, size_t count, loff_t *fops)
 49 {
 50     printk("---------read %s-------------\n",__FUNCTION__);
 51 
 52     int ret;
 53     ret = copy_to_user(buf, &kernal_val, count);
 54     if(ret > 0)
 55     {
 56         printk("copy_to_user");
 57         return -EFAULT;
 58     }
 59     
 60     return 0;
 61 }
 62 
 63 int chr_drv_open (struct inode *inode, struct file *filp)
 64 {
 65     printk("---------open %s-------------\n",__FUNCTION__);
 66     
 67     return 0;
 68 }
 69 
 70 int chr_drv_close (struct inode *inode, struct file *filp)
 71 {
 72     printk("---------close %s-------------\n",__FUNCTION__);
 73     
 74     return 0;
 75 }
 76 
 77 
 78 
 79 const struct file_operations my_fops = {
 80     .open    = chr_drv_open,
 81     .read    = chr_drv_read,
 82     .write   = chr_drv_write,
 83     .release = chr_drv_close,
 84 };
 85 
 86 static int __init chr_dev_init(void)
 87 {
 88     //裝載一般都是申請設備號資源
 89     //申請設備號
 90     int ret;
 91     
 92     ret = register_chrdev(dev_major, "chr_dev_test", &my_fops);
 93     if(ret == 0){
 94         printk("register ok\n");
 95     }
 96     else{
 97         printk("register failed\n");
 98         return -EINVAL;
 99     }
100 
101     //自動創建設備節點
102     devcls = class_create(THIS_MODULE, "chr_cls");
103     dev = device_create(devcls,NULL,MKDEV(dev_major,dev_minor),NULL,"chr2");
104 
105     //對地址進行映射
106     gpx2conf = ioremap(GPX2CON, GPX2_SIZE);  //映射兩個寄存器
107     gpx2dat  = gpx2conf + 1; 
108 
109     printk("After ioremap : gpx2conf = 0x%p, gpx2dat = 0x%p\n",gpx2conf,gpx2dat);
110     
111     //配置GPIO為輸出功能
112     *gpx2conf &= ~(0xf << 28);
113     *gpx2conf |=  (0x1 << 28);
114     
115     return 0;
116 }
117 
118 static void __exit chr_dev_exit(void)
119 {
120      //卸載一般都是釋放資源
121     iounmap(gpx2conf);
122     
123     //銷毀節點和類
124     device_destroy(devcls,MKDEV(dev_major,dev_minor));
125     class_destroy(devcls);
126     //釋放設備號
127      unregister_chrdev(dev_major, "chr_dev_test");
128 }
129 
130 module_init
131 (chr_dev_init);
132 module_exit(chr_dev_exit);
133 MODULE_LICENSE("GPL");
chr_drv1.c
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <sys/stat.h>
 7 #include <fcntl.h>
 8 
 9 
10 int main(int argc, char * argv [ ])
11 {
12     //調用驅動
13     int fd;
14     int value = 0;  //buf
15 
16     fd = open("/dev/chr2", O_RDWR);
17     if(fd < 0)
18     {
19         perror("open");
20         exit(1);
21     }
22 
23     read(fd, &value, 4);   //應用程序從內核獲取4字節數據存放在value中 
24     printf("__USER__: value = %d\n", value);  //打印獲取的值
25     sleep(1);               //sleep1秒,防止串口打印信息不完整
26 
27     //應用程序控制燈
28     while(1)
29     {
30         value = 0;
31         write(fd, &value, 4);
32         sleep(1);
33         
34         value = 1;
35         write(fd, &value, 4);
36         sleep(1);
37     }
38     
39 
40     close(fd);
41     
42     return 0;
43 }
chr_test.c

測試:

 

 執行app后,可以看到LED等以一秒的間隔亮滅

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM