Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

 现在,我们来编写自己第一个字符设备驱动 —— 点亮LED。(不完善,后面再完善)

硬件平台:Exynos4412(FS4412)


编写驱动分下面几步:

a -- 查看原理图、数据手册,了解设备的操作方法;

b -- 在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始;

c -- 实现驱动程序的初始化:比如向内核注册这个驱动程序,这样应用程序传入文件名,内核才能找到相应的驱动程序;

d -- 设计所要实现的操作,比如 open、close、read、write 等函数;

e -- 实现中断服务(中断不是每个设备驱动所必须的);

f -- 编译该驱动程序到内核中,或者用 insmod 命令加载;

g-- 测试驱动程序;


下面是一个点亮LED 的驱动:

第一步,当然是查看手册,查看原理图,找到相应寄存器;


查看手册,四个LED 所用寄存器为:

led2

GPX2CON    0x11000c40
GPX2DAT     0x11000c44

led3

GPX1CON    0x11000c20
GPX1DAT     0x11000c24

led4  3-4 3-5

GPF3CON   0x114001e0
GPF3DAT    0x114001e4


这里要注意:arm体系架构是io内存,必须要映射   ioremap( );  其作用是物理内存向虚拟内存的映射。 用到 writel   readl这两个函数,详细解释会在后面不上,先看一下简单用法:

以LED2为例,下面是地址映射及读写:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. int *pgpx2con  ;  
  2. int *pgpx2dat;  
  3.   
  4. pgpx2con = ioremap( GPX2CON, 4);  
  5. pgpx2dat = ioremap(GPX2DAT,4);  
  6. readl(pgpx2con);  
  7. writel(0x01, pgpx2dat );  

下面是驱动程序,后面会更完善
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5. #include <asm/io.h>  
  6. #include <asm/uaccess.h>  
  7.   
  8. static int major = 250;  
  9. static int minor=0;  
  10. static dev_t devno;  
  11. static struct class *cls;  
  12. static struct device *test_device;  
  13.   
  14. #define  GPX2CON    0x11000c40  
  15. #define  GPX2DAT    0x11000c44  
  16. #define  GPX1CON    0x11000c20  
  17. #define  GPX1DAT    0x11000c24  
  18. #define  GPF3CON    0x114001e0  
  19. #define  GPF3DAT    0x114001e4  
  20.   
  21. static int *pgpx2con  ;  
  22. static int *pgpx2dat;  
  23.   
  24. static int *pgpx1con  ;  
  25. static int *pgpx1dat;  
  26.   
  27. static int *pgpf3con  ;  
  28. static int *pgpf3dat;  
  29.   
  30. void fs4412_led_off(int num);  
  31.   
  32. void fs4412_led_on(int num)  
  33. {  
  34.     switch(num)  
  35.     {  
  36.         case 1:  
  37.             writel(readl(pgpx2dat) |(0x1<<7), pgpx2dat);  
  38.             break;  
  39.         case 2:  
  40.             writel(readl(pgpx1dat) |(0x1<<0), pgpx1dat);            
  41.             break;            
  42.         case 3:  
  43.             writel(readl(pgpf3dat) |(0x1<<4), pgpf3dat);    
  44.             break;  
  45.         case 4:  
  46.             writel(readl(pgpf3dat) |(0x1<<5), pgpf3dat);            
  47.             break;    
  48.         default:  
  49.             fs4412_led_off(1);  
  50.             fs4412_led_off(2);  
  51.             fs4412_led_off(3);  
  52.             fs4412_led_off(4);  
  53.             break;  
  54.               
  55.     }  
  56. }  
  57.   
  58. void fs4412_led_off(int num)  
  59. {  
  60.     switch(num)  
  61.     {  
  62.         case 1:  
  63.             writel(readl(pgpx2dat) &(~(0x1<<7)), pgpx2dat);  
  64.             break;  
  65.         case 2:  
  66.             writel(readl(pgpx1dat)&(~(0x1<<0)), pgpx1dat);              
  67.             break;            
  68.         case 3:  
  69.             writel(readl(pgpf3dat) &(~(0x1<<4)), pgpf3dat);     
  70.             break;  
  71.         case 4:  
  72.             writel(readl(pgpf3dat) &(~(0x1<<5)), pgpf3dat);             
  73.             break;            
  74.     }  
  75. }  
  76.   
  77. static int led_open (struct inode *inode, struct file *filep)  
  78. {//open  
  79.     fs4412_led_off(1);  
  80.     fs4412_led_off(2);  
  81.     fs4412_led_off(3);  
  82.     fs4412_led_off(4);    
  83.     return 0;  
  84. }  
  85.   
  86. static int led_release(struct inode *inode, struct file *filep)  
  87. {//close  
  88.     fs4412_led_off(1);  
  89.     fs4412_led_off(2);  
  90.     fs4412_led_off(3);  
  91.     fs4412_led_off(4);    
  92.     return 0;  
  93. }  
  94.   
  95. static ssize_t led_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)  
  96. {  
  97.     return 0;  
  98. }  
  99.   
  100. static ssize_t led_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)  
  101. {  
  102.     int led_num;  
  103.   
  104.     if(len !=4)  
  105.     {  
  106.         return -EINVAL;  
  107.     }  
  108.     if(copy_from_user(&led_num,buf,len))  
  109.     {  
  110.         return -EFAULT;   
  111.     }  
  112.   
  113.     fs4412_led_on(led_num);  
  114.     printk("led_num =%d \n",led_num);  
  115.   
  116.     return 0;  
  117. }  
  118.   
  119. static struct file_operations hello_ops=  
  120. {  
  121.     .open     = led_open,  
  122.     .release = led_release,  
  123.     .read     = led_read,  
  124.     .write    = led_write,  
  125. };  
  126.   
  127. static void fs4412_led_init(void)  
  128. {  
  129.     pgpx2con = ioremap(GPX2CON,4);  
  130.     pgpx2dat = ioremap(GPX2DAT,4);  
  131.   
  132.     pgpx1con = ioremap(GPX1CON,4);  
  133.     pgpx1dat =ioremap(GPX1DAT,4);  
  134.   
  135.     pgpf3con  = ioremap(GPF3CON,4);  
  136.     pgpf3dat =ioremap(GPF3DAT,4);  
  137.   
  138.     writel((readl(pgpx2con)& ~(0xf<<28)) |(0x1<<28),pgpx2con) ;  
  139.     writel((readl(pgpx1con)& ~(0xf<<0)) |(0x1<<0),pgpx1con) ;     
  140.     writel((readl(pgpf3con)& ~(0xff<<16)) |(0x11<<16),pgpf3con) ;     
  141. }  
  142.   
  143. static int led_init(void)  
  144. {  
  145.     int ret;      
  146.     devno = MKDEV(major,minor);  
  147.     ret = register_chrdev(major,"led",&hello_ops);  
  148.   
  149.     cls = class_create(THIS_MODULE, "myclass");  
  150.     if(IS_ERR(cls))  
  151.     {  
  152.         unregister_chrdev(major,"led");  
  153.         return -EBUSY;  
  154.     }  
  155.     test_device = device_create(cls,NULL,devno,NULL,"led");//mknod /dev/hello  
  156.     if(IS_ERR(test_device))  
  157.     {  
  158.         class_destroy(cls);  
  159.         unregister_chrdev(major,"led");  
  160.         return -EBUSY;  
  161.     }     
  162.     fs4412_led_init();  
  163.     return 0;  
  164. }  
  165.   
  166. void fs4412_led_unmap(void)  
  167. {  
  168.     iounmap(pgpx2con);  
  169.     iounmap(pgpx2dat );  
  170.   
  171.     iounmap(pgpx1con);  
  172.     iounmap(pgpx1dat );  
  173.   
  174.     iounmap(pgpf3con );  
  175.     iounmap(pgpf3dat );  
  176. }  
  177.   
  178. static void led_exit(void)  
  179. {  
  180.     fs4412_led_unmap();  
  181.     device_destroy(cls,devno);  
  182.     class_destroy(cls);   
  183.     unregister_chrdev(major,"led");  
  184.     printk("led_exit \n");  
  185. }  
  186.   
  187. MODULE_LICENSE("GPL");  
  188. module_init(led_init);  
  189. module_exit(led_exit);  

测试程序:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6. main()  
  7. {  
  8.     int fd,i,lednum;  
  9.   
  10.     fd = open("/dev/led",O_RDWR);  
  11.     if(fd<0)  
  12.     {  
  13.         perror("open fail \n");  
  14.         return ;  
  15.     }  
  16.     for(i=0;i<100;i++)  
  17.     {  
  18.         lednum=0;  
  19.         write(fd,&lednum,sizeof(int));  
  20.         lednum = i%4+1;  
  21.         write(fd,&lednum,sizeof(int));    
  22.         sleep(1);  
  23.     }  
  24.     close(fd);  
  25. }  

makefile:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=hello.o  
  3. $(info "2nd")  
  4. else  
  5. #KDIR := /lib/modules/$(shell uname -r)/build  
  6. KDIR := /home/xiaoming/linux-3.14-fs4412  
  7. PWD:=$(shell pwd)  
  8. all:  
  9.     $(info "1st")  
  10.     make -C $(KDIR) M=$(PWD) modules  
  11.     arm-none-linux-gnueabi-gcc test.c  
  12.     sudo cp hello.ko a.out /rootfs/test/  
  13. clean:  
  14.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  15. endif  

编译结束后,将a.out 和 hello.ko 拷贝到开发板中:

# insmod hello.ko

#mknod /dev/hello c 250 0

#./a.out

会看到跑马灯效果。

后面会对该驱动完善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 &#xff08;三&#xff09;—— 字符设备驱动结构&#xff08;中&#xff09; &#xff0c;我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} &#xff0c;下面来介绍另一个比较重要数据结构 struct _file_operatio…

Android开发群

为什么80%的码农都做不了架构师&#xff1f;>>> 我的自建Android应用开发群&#xff0c;欢迎大家来聊聊呀&#xff01;201427584 转载于:https://my.oschina.net/catia/blog/176384

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚&#xff0c;地接的导游非常热情&#xff0c;如同大多数人一样&#xff0c;导游也会在这短短的几天内&#xff0c;尽可能的表现自己&#xff0c;此文聊聊导游说的三句话。 旅游三不“较”&#xff1a; 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法&#xff0c;利用cat /proc/devices查看申请到的设备名&#xff0c;设备号。 第一种是使用mknod手工创建&#xff1a;mknod filename type major minor 第二种是自动创建设备节点&#xff1a;利用udev&#xff08;mdev&#xff09;来实现设备…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

MM引擎新应用——爱车加油记

基于MM应用引擎开发的EXTJS应用&#xff0c;车主每次加完汽油后&#xff0c;记录加油时的里程数以及加油金额和汽油价格&#xff0c;就可计算出上次加油后行驶的里程数、上次加油的平均油耗。点击刷新按钮&#xff0c;即可计算有记录以来的行驶公里数和再次期间加油金额和平均油…

### 阅读之痕-2013/11

2019独角兽企业重金招聘Python工程师标准>>> 阅读之痕-2013/11 Andy erpingwugmail.com 2013/11/01-2013/11/30 2013/11/01-2013/11/30 The story of rocksdb - Embedded key-value store for Flash and RAM http://rocksdb.org/intro.pdf High Performance Network…

Exynos4412 内核移植(六)—— 设备树解析

一、描述ARM Device Tree起源于OpenFirmware (OF)&#xff0c;在过去的Linux中&#xff0c;arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码&#xff0c;相当多数的代码只是在描述板级细节&#xff0c;而这些板级细节对于内核来讲&#xff0c;不过是垃圾&#xff…

Github Page创建个人主页以及绑定域名

2019独角兽企业重金招聘Python工程师标准>>> 在github中 我们可以通过github page创建个人主页 以及绑定域名 据说有300m空间 以及无限流量 不过只能支持静态html 以及一些脚本语言 顺便吐槽一下 阿里云最低配那个云空间服务器 512m内存 启动web服务器后 mys…

Exynos4412 内核移植(五)—— 驱动的移植

以移植自己制作的驱动&#xff0c;学习内核移植中的驱动移植&#xff0c;及 驱动程序的动态编译和静态编译 硬件环境&#xff1a; Linux 内核版本&#xff1a;Linux 3.14 主机&#xff1a;Ubuntu 12.04发行版 目标机&#xff1a;FS4412平台 交叉编译工具&#xff1a;arm-none-l…

FTP文件共传输服务

FTP文件共传输服务一&#xff0c;vsftpd服务基础&#xff08;1&#xff09;&#xff0c;FTP服务概述FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是典型的C/S结构的应用层协议&#xff0c;需要由服务端软件、客户端软件共同实现文件传达输功能…

Exynos4412 内核移植(四)—— MMU 相关知识解析

一、MMU的产生 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以内存容量虽然小&#xff0c;但还是可以容纳当…

Mysql limit 子查询

为什么80%的码农都做不了架构师&#xff1f;>>> &#xff08;1&#xff09;mysql limit 不支持子查询像下面这条语句无法执行 SELECT * FROM b_c_s1 where CT_ID IN (SELECT DISTINCT CT_ID FROM b_c_s1 LIMIT 0,2); &#xff08;2&#xff09;应该在只查询外面再…

Exynos4412 内核移植(二)—— 内核编译过程分析

内核的编译同样是从Makefile 来分析&#xff1a; 一、内核源码结构 Linux内核文件数目近2万&#xff0c;出去其他架构CPU的相关文件&#xff0c;他们分别位于顶层目录下的17个子目录&#xff0c;各个目录功能独立&#xff0c;下面是常用目录&#xff1a; arch:体系结构相关代码…

深入理解Java:注解(Annotation)

2019独角兽企业重金招聘Python工程师标准>>> 一、概述 1.什么是注解&#xff08;Annotation&#xff09; Annotation&#xff08;注解&#xff09;就是Java提供了一种元程序中的元素关联任何信息和着任何元数据&#xff08;metadata&#xff09;的途径和方法。Ann…