CubieBoard中文论坛

 找回密码
 立即注册
搜索
热搜: unable
查看: 7578|回复: 8

简单platform总线设备驱动模型——GPIO控制

[复制链接]
发表于 2014-1-6 11:06:58 | 显示全部楼层 |阅读模式
本帖最后由 @allen 于 2014-1-6 11:15 编辑


原文章 http://blog.csdn.net/RubyBoss/article/details/7729927  我加了自己注释和  A10或A20寄存器相关的内容

设备代码:

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

#define GPIO_BASE 0x01C20800    // GPIO基地址
#define GPH_CFG2        (GPIO_BASE + 0x104)   // PH20 PH21(板子上面的绿灯和蓝灯)GPIO配置寄存器
#define GPH_DATA        (GPIO_BASE + 0x10c)   // PH20 PH21 数据寄存器

选区_007.png

选区_009.png




选区_010.png
  1. /*定义资源,可以在平台总线驱动程序里通过platform_get_resource()函数来获得资源*/
  2. static struct resource led_resource[] = {
  3.     [0] = {
  4.         .start = CFG2,          //起始地址
  5.         .end   = GPH_DATA,    // 结束地址
  6.         .flags = IORESOURCE_MEM, //表示是内存资源,使用的时候需要ioremap的
  7.     },
  8.     [1] = {
  9.         .start = 5,      //??
  10.         .end   = 5,      // ??
  11.         .flags = IORESOURCE_IRQ,   //表示是中断资源,不用映射
  12.     }

  13. };

  14. static void led_release(struct device * dev)  //这个函数是必须的,但里面可以什么都不做
  15. {
  16.    
  17. }


  18. static struct platform_device led_dev = {
  19.     .name     = "myled",               //这里的设备名要和驱动里的驱动名相同的
  20.     .id           =  -1,                                          
  21.     .num_resources    = ARRAY_SIZE(led_resource),   //数组 led_resource 大小
  22.     .resource              = led_resource,                  //这里就是刚刚定义的资源
  23.     .dev = {
  24.                   .release  = led_release,   //注册release函数,应该是 platform_device_unregister的时候会调用
  25.                },
  26. };


  27. static int led_dev_init(void)
  28. {
  29.      platform_device_register(&led_dev);    //注册平台设备
  30.      return 0;
  31. }


  32. static void led_dev_exit(void)
  33. {
  34.       platform_device_unregister(&led_dev);   //卸载平台设备
  35. }


  36. module_init(led_dev_init);
  37. module_exit(led_dev_exit);
  38. MODULE_LICENSE("GPL");



复制代码
回复

使用道具 举报

 楼主| 发表于 2014-1-6 11:06:59 | 显示全部楼层
本帖最后由 @allen 于 2014-6-6 18:10 编辑


再来看一下驱动代码:


#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPH20_CFG        (1 << 20)
#define GPH21_CFG        (1 << 16)

volatile unsigned  long  *gph_cfg2 = NULL;
volatile unsigned  long  *gph_date = NULL;
static struct class *cls;
static int major;

/*将引脚设置为输出引脚*/
static int  led_open(struct inode *inode, struct file *file)
{        /*将引脚设置为输出引脚*/

              *gph_cfg2 &= ~(GPH20_CFG);
              *gph_cfg2 |= GPH20_CFG;
              *gph_cfg2 &= ~(GPH21_CFG);
              *gph_cfg2 |= GPH21_CFG;

              printk(KERN_ALERT"led openi\n");      
              return 0;
}

/*根据用户空间里传进来的参数点亮或者熄灭led*/
static int led_write (struct file *file, const char __user *buff, size_t count, loff_t *ppos)
{
              int val;
              copy_from_user(&val, buff, count);
         if (val == 0){
                *gpio_dat &= ~(0x3<<20);     // 关 绿 蓝灯
                            }
        else{
                *gpio_dat |= (0x3<<20);          // 亮 绿 蓝灯
               }
            return 0;
}

static struct file_operations led_fops={
          /*主要的工作放在了这个地方*/
         .owner  =   THIS_MODULE,     //  这是一个宏,推向编译模块时自动创建的__this_module变量
         .open   =   led_open,             //   配置寄存器
         .write  = led_write,               //    对寄存器写数
};

static int led_probe(struct platform_device *pdev)     //匹配之后会将设备作为参数传进来的哦
{
       struct resource *res;
       res=platform_get_resource(pdev, IORESOURCE_MEM, 0);       //获取设备里定义的内存资源
       gph_cfg2 = (volatile unsigned long*)ioremap(res->start,12);    //内存资源需要映射的哦

      gph_date = gph_cfg2 + 2;    // 0x104+0x8  , int 2 对应 0x 8

       printk("led_probe, found led\n");
       major=register_chrdev(0, "myled", &led_fops);  //注册字符设备,那么下面肯定重点放在了file_operations结构体上了
       cls = class_create(THIS_MODULE, "myled");     //创建类
       class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led");//类下创建设备,这样保证了udev机制可以自动创建设备文件
       return 0;
   
}

/*与分析相一致,卸载函数放在了这里*/
static int led_remove(struct platform_device *pdev)
{
       printk("led_remove, remove led\n");
       iounmap(gph_cfg2);
       unregister_chrdev(major, "myled");          // 卸载
       device_destroy(leddrv_class,MKDEV(major,0));
       class_destroy(cls);
       return 0;
}

}

/*定义platform_driver 结构体*/
struct platform_driver led_drv = {
       .probe = led_probe,              //这里是probe函数,当总线发现有设备的名字和驱动的名字相同时会调用这个函数,所以我们的主要工作放在了这里面
       .remove = led_remove,        //这个函数应该是在platform_driver_unregister是调用用,因此可以将一些卸载函数放在这个地方
       .driver = {
                   .name = "myled",    //这名字要和设备相匹配的哦
                      }
};

static int led_drv_init(void)
{
         platform_driver_register(&led_drv);      //注册平台总线驱动,里面有比较重要的platform_driver 结构体
         return 0;
}

static void led_drv_exit(void)
{
         platform_driver_unregister(&led_drv);    //  卸载平台总线驱动
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");


回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-1-6 11:07:00 | 显示全部楼层
本帖最后由 @allen 于 2014-1-6 11:22 编辑

下面再来看看测试程序:


  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>

  5. int main(int argc, char **argv)
  6. {
  7. int fd;
  8. int val = 1;
  9. fd = open("/dev/led", O_RDWR);
  10. if (fd < 0)
  11. {
  12. printf("can't open!\n");
  13. }
  14. if (argc != 2)
  15. {
  16. printf("Usage :\n");
  17. printf("%s <on|off>\n", argv[0]);
  18. return 0;
  19. }

  20. if (strcmp(argv[1], "on") == 0)
  21. {
  22. val  = 1;
  23. }
  24. else
  25. {
  26. val = 0;
  27. }
  28. write(fd, &val, 4);
  29. return 0;
复制代码
测试方法:假设测试程序编译出来后名字是ledtest
我们在命令行输入:./ledtest /dev/led on            则灯亮
                  输入:./ledtest /dev/led off            则灯灭

我们来总结一下这三个程序的原理:
比如我们先注册了设备,这时总线会遍历驱动,寻找可以匹配该设备的驱动。由于我们还没有注册驱动,所以这时候不会发生什么事情。接着我们注册了驱动,这时会去遍历设备,这时因为设备已经注册了,所以会找到匹配项,这时将调用驱动中的probe函数,在这个函数里完成了字符设备的注册,以及设备文件的自动生成。然后运行我们的测试程序就可以对字符设备进行操作了。
那我们想想这种机制有什么好处呢?其实这就是采用了一种分离的机制,将设备和驱动分开,当我们想操作其它设备的时候,只需要修改设备文件就可以了!比如我们想操作其它的led,那么只需将设备文件里的资源改掉就可以了,并不需要修改驱动文件。这就为一只提供了很大的方面,后面会深有体会的哦!

以上的代码我没编译过,代码可能有错。本帖只是对 cubieboard 下platform设备驱动的学习,大概粗略地讲解代码,真正的驱动代码复杂更多,我也看不太懂。本人也是正在学习驱动,所以将学习总结贴出来给新手,也希望更多的人分享知识。大多的驱动代码将设备代码和驱动代码写在一起,为了简单容易看懂,我就分开来分析,更多的讲解需要自己找资料看看,错误之处也请大侠指出。
回复 支持 反对

使用道具 举报

发表于 2014-1-6 14:45:29 | 显示全部楼层
{:soso_e179:} 好东西..
回复 支持 反对

使用道具 举报

发表于 2014-1-6 15:07:02 | 显示全部楼层
哈哈,你比我快,我刚想分享我的。。。我和你写的大致差不多
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-1-7 15:07:49 | 显示全部楼层
shawnlee 发表于 2014-1-6 15:07
哈哈,你比我快,我刚想分享我的。。。我和你写的大致差不多

将你成功的代码发帖出来了吧,我的肯定有问题的
回复 支持 反对

使用道具 举报

发表于 2014-1-17 17:59:18 | 显示全部楼层
论坛分享驱动的帖子很少的...  不过gpio-sunxi.c里面的东西,不敢恭维。
回复 支持 反对

使用道具 举报

发表于 2014-1-17 18:03:52 | 显示全部楼层
@allen 发表于 2014-1-6 11:07
下面再来看看测试程序:测试方法:假设测试程序编译出来后名字是ledtest
我们在命令行输入:./ledtest /dev ...
  1. 大多的驱动代码将设备代码和驱动代码写在一起,为了简单容易看懂,我就分开来分析,更多的讲解需要自己找资料看看,错误之处也请大侠指出。
复制代码
在  linux-3.4/arch/arm/mach-sun7i/  目录下,有个 core.c 和 devices.c,这里告诉我们,设备代码和驱动代码是分开的。个人感觉是这也是嵌入式驱动的常态--- 可能嵌入式并不怎么支持热插拔。

我个人的理解。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-1-17 18:13:30 | 显示全部楼层
醉月 发表于 2014-1-17 18:03
在  linux-3.4/arch/arm/mach-sun7i/  目录下,有个 core.c 和 devices.c,这里告诉我们,设备代码和驱动 ...

谢谢提醒,等我对驱动再深入学习后,再回来改正所有错误
回复 支持 反对

使用道具 举报

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

本版积分规则

QQ|Archiver|手机版|粤ICP备13051116号|cubie.cc---深刻的嵌入式技术讨论社区

GMT+8, 2024-11-24 01:51 , Processed in 0.027002 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2012 Comsenz Inc. | Style by Coxxs

返回顶部