5429|4

241

帖子

4

TA的资源

纯净的硅(初级)

楼主
 

Helper2416-35——Linux驱动——第一个驱动程序(点亮LED) [复制链接]

本帖最后由 yuanlai2010 于 2014-8-26 14:29 编辑

Linux驱动——第一个驱动程序(点亮LED)
参与Helper2416开发板助学计划心得

经过几天的琢磨,今天终于完成了我的第一个驱动,也算是敲开了驱动编写的大门

述:
Linux驱动程序大致分为三种:字符设备驱动、块设备驱动、网络驱动,.这个LED驱动程序就是最简单的字符驱动程序中的最简单的一个实例,不过却也涵盖了驱动程序开发的基本步骤。

先上源码,然后再一点点解剖:
  1. #include <linux/module.h>
  2. #include <linux/types.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/platform_device.h>
  6. #include <linux/device.h>
  7. #include <linux/io.h>
  8. #include <linux/miscdevice.h>
  9. #include <linux/uaccess.h>

  10. #include <asm/uaccess.h>
  11. #include <asm/atomic.h>
  12. #include <asm/unistd.h>

  13. static int LED_Major = 0;
  14. static struct class *led_driver_class;
  15. volatile unsigned long *GPBCON, *GPBDAT;


  16. static int s3c24xx_led_open(struct inode *inode, struct file *file)
  17. {
  18.         printk("s3c24xx_led_open\n");
  19.         (*GPBCON) |= 1<<2;
  20.     return 0;
  21. }

  22. static int s3c24xx_led_close(struct inode *inode, struct file *file)
  23. {
  24.         printk("s3c24xx_led_close\n");
  25.     return 0;
  26. }

  27. static int s3c24xx_led_write(struct file *file, char __user *buff, size_t count, loff_t *offp)
  28. {
  29.     int  value;
  30.         
  31.         copy_from_user(&value, buff, count);

  32.         if(value == 1){
  33.                 (*GPBDAT) &= ~(1<<1);
  34.         }else if(value == 0){
  35.                 (*GPBDAT) |= 1<<1;
  36.         }else{
  37.                 printk("zhe writed value must be 1 or 0\n");
  38.                 return 1;
  39.         }
  40.     return 0;
  41. }

  42. static struct file_operations led_fops = {
  43.     .owner   =   THIS_MODULE,
  44.     .open    =   s3c24xx_led_open,
  45.         .write         =         s3c24xx_led_write,
  46.     .release =   s3c24xx_led_close,
  47. };

  48. static int __init led_init(void)
  49. {
  50.     LED_Major = register_chrdev(LED_Major, "led_driver", &led_fops);
  51.         led_driver_class = class_create(THIS_MODULE, "led_driver");
  52.         device_create(led_driver_class, NULL, MKDEV(LED_Major, 0), NULL, "led_driver");
  53.         GPBCON = (volatile unsigned long*)ioremap(0x56000010,8);
  54.         GPBDAT = GPBCON + 1;
  55.         printk("led_init\n");
  56.     return 0;
  57. }

  58. static void __exit led_exit(void)
  59. {
  60.     unregister_chrdev(LED_Major, "led_driver");
  61.         device_destroy(led_driver_class, MKDEV(LED_Major, 0) );
  62.         class_destroy(led_driver_class);
  63.         iounmap(GPBCON);
  64.         printk("led_exit\n");
  65. }

  66. module_init(led_init);
  67. module_exit(led_exit);
  68. MODULE_AUTHOR("yuanlai");
  69. MODULE_DESCRIPTION("helper2416 led Driver");
  70. MODULE_LICENSE("GPL");
复制代码

第一步:搭好驱动程序的框架

首先就是头文件:
头文件比较多,暂且还不知道这些头文件是不是都有用到,但是至少驱动能够编译成功。

接着就是structfile_operations 这个结构体及驱动处理函数:
加载驱动的时候,就需要把这个struct file_operations的结构体注册进内核,该结构体在头文件linux/fs.h定义,应该是告诉系统这个驱动程序具体有哪些函数用来操作硬件的,然后内核就是通过这个结构体的成员(函数指针)来找到这些函数来操作硬件的,以下便是这个结构体看起来的样子:
  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(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
  6.   ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
  7.   ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
  8.   int (*readdir) (struct file *, void *, filldir_t);
  9.   unsigned int (*poll) (struct file *, struct poll_table_struct *);
  10.   int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  11.   int (*mmap) (struct file *, struct vm_area_struct *);
  12.   int (*open) (struct inode *, struct file *);
  13.   int (*flush) (struct file *);
  14.   int (*release) (struct inode *, struct file *);
  15.   int (*fsync) (struct file *, struct dentry *, int datasync);
  16.   int (*aio_fsync) (struct kiocb *, int datasync);
  17.   int (*fasync) (int, struct file *, int);
  18.   int (*lock) (struct file *, int, struct file_lock *);
  19.   ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
  20.   ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
  21.   ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);
  22.   ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  23.   unsigned long (*get_unmapped_area) (struct file *, unsigned long,
  24.          unsigned long, unsigned long,
  25.          unsigned long);
  26. };
复制代码
对于具体要用到哪些成员,就需要根据驱动程序的具体情况来决定了。不过.owner   =   THIS_MODULE, 这条是一定要有的。由于只是控制一盏LED的亮灭状态,所以我也只用到了open、write、close这几个成员。

最后就是模块初始化与卸载处理函数:
模块的初始化这个函数(led_init)是在insmod的时候自动运行的,在代码中还需要用module_init()这个宏来包装一下。在这个函数中就需要把上面提到的structfile_operations这个结构体注册到内核里面去,这里使用register_chrdev()函数完成注册。
接着还需要在/dev/目录下创建相应设备节点,尽管可以通过shell用命令手工创建,但那是在太麻烦了点,所以通过先创建一个类,然后再在这个类下创建一个所需要的设备文件。
同样的模块退出函数(led_exit())是在rmmod的时候自动运行的,同样也需要module_exit()这个宏来包装一下。这里需要注销之前注册的structfile_operations结构体,然后销毁之前创建的类和设备(删除/dev/目录下的设备节点)

忘了,在最后的最后,还需要加上这些信息,尤其是MODULE_LICENSE("GPL");这条,不然很多时候编译会出错。
  1. MODULE_AUTHOR("yuanlai");
  2. MODULE_DESCRIPTION("helper2416 led Driver");
  3. MODULE_LICENSE("GPL");
复制代码


第二部:完善硬件操作
由于这仅仅是一个最简单的一盏LED的驱动,所以硬件操作比较简单,不过由于Linux操作的都是虚拟地址,所以当操作实际硬件的时候,就需要把物理地址映射成虚拟地址,具体的虚拟地址是多少这个是由系统来决定的,我们可以通过ioremap()这个函数来获得硬件物理地址所对应的虚拟地址,当然在驱动卸载时也记得用iounmap()解除此次映射。为了方便,我在驱动加载的时候,就用ioremap完成了IO的映射,并在led_exit()中用iounmap取消映射。然后具体的硬件操作都体现在s3c24xx_led_open()、s3c24xx_led_write()这两个函数里面了,怎么样,是不是和裸机差不多?

第三步:编译模块(驱动)
编译模块是一定需要有内核源码树的,我这里直接使用的是BOSS提供的Fedora18虚拟机镜像里的/home/jyxtec/workspace/kernel/s3c-linux
下面是Makefile文件具体内容,直接执行make后就在当前目录生成了我期待已久的.ko文件了。
  1. obj-m := led_module.o
  2. KERNEL_DIR :=/home/jyxtec/workspace/kernel/s3c-linux
  3. PWD := $(shell pwd)
  4. all:
  5.         make ARCH=arm CROSS_COMPILE=arm-linux- -C /home/jyxtec/workspace/kernel/s3c-linux M=$(PWD) modules
  6. clean:
  7.         rm *.o *.ko
复制代码


驱动测试
拿到.ko文件后当然就是急着去测试咯,只有测试成功了才是真正值得兴奋的时刻。

测试程序代码如下,实现频率为2秒的LED闪烁。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <fcntl.h>
  6. #include <sys/stat.h>

  7. int main(int argc, char **argv)
  8. {
  9.         int fd;        
  10.         int value;

  11.         fd = open("/dev/led_driver", O_WRONLY);
  12.         if(fd < 0)
  13.         {
  14.                 perror("cannot open device led");
  15.                 exit(1);
  16.         }        
  17.         while(1)
  18.         {
  19.                 /* 点亮 LED */
  20.                 value = 1;
  21.                 write(fd, &value, 4);
  22.                 sleep(1);
  23.                
  24.                 /* 熄灭 LED */
  25.                 value = 0;
  26.                 write(fd, &value, 4);
  27.                 sleep(1);
  28.         }
  29.         close(fd);
  30.         return 0;

  31. }
复制代码

首先需要把.ko 文件和测试程序的可执行文件发送到目标版(Helper2416)
  1. [root@jyxtec Linux]# ls                                                         
  2. led_module.ko  led_test                                                         
  3. [root@jyxtec Linux]#
复制代码

然后执行 insmod led_module.ko 打印出以下信息,模块应该是已经加载成功了。
  1. [root@jyxtec Linux]# insmod led_module.ko                                       
  2. led_init                                                                        
  3. [root@jyxtec Linux]#
复制代码

为了验证是否整的已经加载,我们再来查看/dev/目录是否有我们所期待的设备文件 led_driver ., 确实是有的。
  1. [root@jyxtec Linux]# ls -l /dev/led*                                            
  2. crw-rw----    1 root     root      252,   0 Jan  1 03:40 /dev/led_driver        
  3. [root@jyxtec Linux]#
复制代码

接着就是运行测试程序了,由于我在驱动程序的open函数中加入打印信息的语句,所以会打印下面这些信息,然后开发板上的LED已经闪烁起来,由于测试程序是个无限循环,所以最后只能通过Ctle + C 结束程序。

测试成功,第一个驱动程序成功的运行起来了!                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
论坛ID:yuanlai2010
发表时间:2014-08-26

最新回复

恭喜恭喜!  详情 回复 发表于 2014-8-27 12:19
点赞 关注

回复
举报

241

帖子

4

TA的资源

纯净的硅(初级)

沙发
 
悄悄的在二楼放上源码走人      LED_Module.rar (1.6 KB, 下载次数: 11)
 
 

回复

2144

帖子

3

TA的资源

五彩晶圆(中级)

板凳
 
不错都开始搞驱动了
 
个人签名电工
 
 

回复

1119

帖子

0

TA的资源

一粒金砂(中级)

4
 
恭喜恭喜呀
 
 
 

回复

554

帖子

0

TA的资源

版主

5
 
恭喜恭喜!
 
个人签名My dreams will go on...
http://www.jyxtec.com
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表