单片机
返回首页

创建一个字符设备1.2

2025-02-17 来源:cnblogs

          本文接着《创建一个字符设备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

  2 #include

  3 #include

  4 #include

  5 #include

  6 #include

  7 #include

  8 #include

  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_openn');

 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_closen');

 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 failn');

 47         return -EFAULT;

 48     }

 49     if(kbuf[1])

 50     {

 51         *(unsigned int *)gpj2dat_va &=~(1< 52     }

 53     else

 54     {

 55         *(unsigned int *)gpj2dat_va|=  (1< 56         

 57     }

 58     

 59 

 60     printk('gec210_led_write,kbuf[0]=%d,kbuf[1]=%dn',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 failn');

 79         rt = alloc_chrdev_region(&led_num,0,1,'gec210_led');   //动态注册设备号

 80         if(rt < 0)

 81         {

 82             printk('alloc_chrdev_region failn');            

 83             return rt;            

 84         }        

 85         led_major = MAJOR(led_num);    // 获取主设备号    

 86         led_minor = MINOR(led_num);    // 获取次设备号    

 87         printk('led_major=%d,led_minor=%dn',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 failn');        

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 failn');    

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=%pn',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 drivern');

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

 2 #include

 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


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章