创建一个字符设备1.2

  

          本文接着《创建一个字符设备1.1》讲如何创建字符设备模型、怎么创建设备文件

=============================================================================================

    创建字符设备流程

       1.定义一个cdev
  2.申请设备号
    .静态注册
    .MKDEV
    .register_chrdev_region

    .动态注册
    .alloc_chrdev_region

  3.定义file_operations,并且初始化

  4.cdev初始化

    .cdev_init

  5.将cdev加入到内核

    .cdev_add


  6.创建设备文件
    .手动创建设备文件,去/dev目录进行创建
    .自动创建

 

  前面《创建一个字符设备1.1》已经讲了初始化驱动函数、卸载驱动函数。

这里多了设备、设备号(主次设备号)、为应用层提供操作设备驱动的方法集、设备文件。

------------------------------------------------------------------------------------------------------------

    针对上面的内容,下面会一一讲解。

  1.我们先看设备文件,在/dev 目录下可查看当前挂载的设备文件

  [root@Mr.Jin Fa /dev]# ls -l

  c: 字符设备                10主设备号   242次设备号          CEC设备文件名
  crw-rw---- 1 root root 10,     242 Jan 1 16:04     CEC
  crw-rw---- 1 root root 10,     243 Jan 1 16:04     HPD
  crw-rw---- 1 root root 14,      12 Jan 1 16:04      adsp

设备文件是驱动层用来给应用层调用(调用应用层调用open()获取文件描述符时)

 

  2.设备是一个由硬件抽象出来的“实体”,它具有完成驱动程序功能。用lsmod查看当前挂载的设备

[root@Mr.Jin Fa /]# lsmod
rtnet3070ap   24124 0   -   Live   0xbf0d3000
rt3070ap     488261   1   rtnet3070ap, Live 0xbf044000 (P)
led_drv     1203 0   -     Live     0xbf028000
buzzer_drv    1488 0   -     Live      0xbf022000

........

    3.设备号是作为区分驱动的标识符,它如进程的PID。

主设备号:用来区分不同硬件类型,如网络设备和串口设备它们的主设备号很大程度是不同的

(为什么这样说呢,因为还是一个原则保证设备号不同就好了)。

次设备号:用来区分同一硬件类型下的不同硬件,如串口0和串口1硬件类型相同,它们的主设备号

相同,但是次设备号不会相同。

设备号:通过调用MKDEV(adc_major,adc_minor)函数输入想要申请的主设备号、次设备号。返回设备号

 

  4.用于层操作驱动方法集和

  static const struct file_operations gec210_led_fops = {
  .owner = THIS_MODULE,        申明这个驱动属于本内核
  .write  = gec210_led_write,      定义用作写函数
  .open = gec210_led_open,  定义打开驱动函数
  .release = gec210_led_close,   定义用于关闭驱动函数
};

=========================================================================

        下面贴代码细讲

  1 #include <linux/errno.h>
  2 #include <linux/kernel.h>
  3 #include <linux/module.h>
  4 #include <linux/cdev.h>
  5 #include <linux/fs.h>
  6 #include <linux/uaccess.h>
  7 #include <linux/ioport.h>
  8 #include <linux/io.h>
  9 
 10   static  struct cdev gec210_led_drv;  // 定义一个设备
 11   unsigned int major=238,minor=0;      // 定义并初始化表示主次设备号的变量
 12   unsigned int led_num=0;       // 定义设备号
 13   int rt=0;              // 定义表示错误返回码
 14 
 15 static struct resource *led_res;      // ioremap()后的虚拟地址的首地址
 16 static void __iomem * gpj2con_va;   // 表示gpj2con_va虚拟地址变量
 17 static void __iomem * gpj2dat_va;   // 表示gpj2dat_va 虚拟地址变量
 18 
 19 
 20 int gec210_led_open (struct inode * inode, struct file *file)
 21 {
 22     printk("gec210_led_open\n");
 23     //将GPJ2CON配置为输出模式
 24     *(unsigned int *)gpj2con_va &=~0xFFFF;
 25     *(unsigned int *)gpj2con_va |= 0x1111;    
 26     *(unsigned int *)gpj2dat_va |= 0xF;        
 27         
 28     return 0;
 29 }
 30 
 31 
 32 int gec210_led_close (struct inode *inode, struct file *file)
 33 {
 34     printk("gec210_led_close\n");
 35     return 0;    
 36     
 37 }
 38 
 39 ssize_t gec210_led_write (struct file *file, const char __user *buf, size_t len, loff_t *offset)
 40 {
 41     char kbuf[2]={0};
 42     if(len > 2)
 43         return -EINVAL;
 44     rt = copy_from_user(kbuf,buf,len);
 45     if(rt !=0){
 46         printk("copy_from_user fail\n");
 47         return -EFAULT;
 48     }
 49     if(kbuf[1])
 50     {
 51         *(unsigned int *)gpj2dat_va &=~(1<<kbuf[0]);
 52     }
 53     else
 54     {
 55         *(unsigned int *)gpj2dat_va|=  (1<<kbuf[0]);        
 56         
 57     }
 58     
 59 
 60     printk("gec210_led_write,kbuf[0]=%d,kbuf[1]=%d\n",kbuf[0],kbuf[1]);
 61     return len;    
 62 }
 63 
 64 static const struct file_operations gec210_led_fops={
 65     .owner       = THIS_MODULE,        // 代表改驱动适用于本内核
 66     .open          = gec210_led_open,   // 定义为应用层提供的打开驱动函数 
 67     .release     = gec210_led_close,  // 定义为应用层提供的关闭驱动函数
 68     .write         = gec210_led_write,  // 定义为应用层提供的写函数
 69 };
 70 
 71 int __init gec210_led_init(void)
 72 {
 73     //申请设备号
 74     led_num=MKDEV(led_major,led_minor);
 75     rt = register_chrdev_region(led_num,1,"gec210_led");
 76     if(rt < 0)
 77     {
 78         printk("register_chrdev_region fail\n");
 79         rt = alloc_chrdev_region(&led_num,0,1,"gec210_led");   //动态注册设备号
 80         if(rt < 0)
 81         {
 82             printk("alloc_chrdev_region fail\n");            
 83             return rt;            
 84         }        
 85         led_major = MAJOR(led_num);    // 获取主设备号    
 86         led_minor = MINOR(led_num);    // 获取次设备号    
 87         printk("led_major=%d,led_minor=%d\n",led_major,led_minor);        
 88     }
 89     
 90     //字符设备初始化
 91     cdev_init(&gec210_led_cdev,&gec210_led_fops);    
 92     //字符设备加入内核
 93     rt = cdev_add(&gec210_led_cdev,led_num,1);    
 94     if(rt < 0){
 95         goto fail_cdev_add;
 96         
 97     }        
 98     //申请物理内存区,起始地址为0xE0200280,申请内存空间为8个字节,登记名字为:GPJ2_LED
 99     led_res=request_mem_region(0xE0200280,8,"GPJ2_LED");    
100     if(led_res == NULL){
101         printk("request_mem_region 0xE0200280 fail\n");        
102         rt = -EBUSY;        
103         goto fail_request_mem_region;    
104     }
105     
106     //IO内存的动态映射
107     gpj2con_va=ioremap(0xE0200280,8);    
108     if(gpj2con_va == NULL){
109         printk("ioremap 0xE0200280 fail\n");    
110         rt = -EBUSY;        
111         goto fail_ioremap;                    
112     }
113     
114     gpj2dat_va = gpj2con_va + 4;    
115     printk("hello gec210 led driver,gpj2con_va=%p,gpj2dat_va=%p\n",gpj2con_va,gpj2dat_va);        
116     return 0;
117     
118 fail_ioremap:
119     release_mem_region(0xE0200280,8);  
120     
121 fail_request_mem_region:
122     cdev_del(&gec210_led_cdev);
123     
124 fail_cdev_add:    
125     unregister_chrdev_region(led_num,1);        
126     
127     return rt;
128 }
129 
130 void __exit gec210_led_exit(void)
131 {
132     iounmap(gpj2con_va);
133     release_mem_region(0xE0200280,8);
134     cdev_del(&gec210_led_cdev);
135     unregister_chrdev_region(led_num,1);
136     printk("exit gec210 led driver\n");
137 }
138 
139 
140 module_init(gec210_led_init);    //insmod
141 module_exit(gec210_led_exit);    //rmmod
142 
143 MODULE_AUTHOR("stephen.wen");
144 MODULE_DESCRIPTION("S5PV210 LED driver");
145 MODULE_LICENSE("GPL");

===================================================================================================

            应用层代码

 1 #include <stdio.h>
 2 #include <fcntl.h>
 3 
 4 int main(void)
 5 {
 6     int fd;
 7     int ret;    
 8     char led0_on_buf[2]={0,1};        //0,led的号码;1,亮
 9     char led0_off_buf[2]={0,0};        //0,led的号码;0,灭    
10     fd = open("/dev/led_drv", O_RDWR);
11     if(fd < 0){
12         perror("open /dev/");
13         return -1;    
14     }    
15     while(1)
16     {
17         //
18         write(fd,led0_on_buf,2);
19         sleep(1);    
20         
21         //
22         write(fd,led0_off_buf,2);
23         sleep(1);        
24     }
25     close(fd);    
26     return 0;
27 }

  由于我们没有做杂项设备、类设备所以我们必须在/dev 目录下新建设备文件

先看加载驱动 insmod 

[root@Mr.Jin Fa /]# insmod LedDrv.ko
[ 4480.789028] hello gec210 led driver,gpj2con_va=e09de280,gpj2dat_va=e09de284

在/dev 目录下建设备文件

[root@Mr.Jin Fa /]# insmod /dev/led_drv c 250 0

[root@Mr.Jin Fa /dev]# ls -l | grep "led_drv"
crw-r--r-- 1 root root 250, 0 Jan 12 19:13 led_drv

     然后我们之间运行程序就好了, 下面是效果:

[root@GEC210 /]# chmod 777 led_test
[root@GEC210 /]# ./led_test
[ 4835.601157] gec210_led_open
[ 4835.601205] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4836.601335] gec210_led_write,kbuf[0]=0,kbuf[1]=0
[ 4837.601422] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4838.601510] gec210_led_write,kbuf[0]=0,kbuf[1]=0
[ 4839.601599] gec210_led_write,kbuf[0]=0,kbuf[1]=1
[ 4840.601686] gec210_led_write,kbuf[0]=0,kbuf[1]=0
^C[ 4841.497062] gec210_led_close

 

posted @ 2017-05-17 16:31  得了白学症  阅读(1848)  评论(0编辑  收藏  举报