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 plain  copy
  在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 plain  copy
  在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 plain  copy
  在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 plain  copy
  在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

会看到跑马灯效果。

后面会对该驱动完善。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值