嵌入式Linux驱动 GPIO操作改进 包括从驱动到测试程序

2019-07-12 21:52发布

class="markdown_views prism-github-gist">

嵌入式Linux驱动 GPIO操作改进 包括从驱动到测试程序

说明

这篇博客记录的是上一次编写GPIO驱动的改进版本。这里改进了两个地方:
  • 面向对象的编程思想 结构体来表示一个对象
  • 做出错判断
    • 在某个位置出错,就要在那儿释放申请的内存

字符的驱动编写小结

字符设备驱动编写步骤

这里出现的代码是从后面的程序拷贝出来的。
字符设备驱动编写步骤:
  • 实现加载和卸载的入口函数
    • module_init(chr_io_init);
    • module_exit(chr_io_exit);
  • 模块加载入口函数中:
  • 申请主设备号(用于区分和管理不同的字符设备)
    • register_chrdev(DEV_MAJOR , “io”,&file_ops);
  • 创建设备节点文件(为用户提供一个可操作的文件接口)
    • struct class* class_create(THIS_MODULE, “io_cls”);
    • struct device* device_create(devcls, NULL, dev_num, NULL, “io”);
  • 硬件的初始化
    • 地址的映射
      ioremap(GPIOL2_CON, LEN);
    • 中断的申请
    • 实现硬件的寄存器初始化
  • 实现file_operations(文件接口的实现)

操作寄存器的方式总结

  • 操作寄存器地址的方式
    • 举例:volatile unsigned long *gpxcon;
    • gpxcon &=~(1<<10);
    • static inline u32 readl(const volatile void __iomem *addr)
      //从寄存器读值
      static inline void writel(unsigned int b, volatile void __iomem *addr)
      //向寄存器写值
    • 举例(这里的代码是会在后面出现的)
u32 value = readl(led_dev->reg_virt_base); value &=~(1<<0); value |=1<<0; writel(value, led_dev->reg_virt_base); gpiol20_dat = gpiol20_cof+1; writel(read(led_dev->reg_virt_base+4) |(1<<0), led_dev->reg_virt_base+4);

代码

#include #include #include #include #include #include #include #include #define GPIOL2_CON 0x11000000+0x0100 #define LEN 8 /* 用一个类来管理 */ struct led_desc{ unsigned int dev_major; struct class *cls; struct device *dev; void* reg_virt_base; }; static struct led_desc *led_dev; ssize_t chr_drv_read (struct file *filp, char __user *buffer, size_t count, loff_t *fpos) { u32 value=0; int ret; printk("-------%s------- ",__FUNCTION__); value = readl(led_dev->reg_virt_base+4); value &=1<<0; ret = copy_to_user(buffer, &value, count); return 0 ; } ssize_t chr_drv_write (struct file *flip, const char __user *buffer, size_t count, loff_t *fpos) { int ret; int value; static int num=0; printk("write:%d ",num++); ret = copy_from_user(&value, buffer, count); if(value == 1) { printk("vaule:%d ",value); writel(readl(led_dev->reg_virt_base+4) |(1<<0), led_dev->reg_virt_base+4); } else { printk("vaule:%d ",value); writel(readl(led_dev->reg_virt_base+4) & ~(1<<0), led_dev->reg_virt_base+4); } return 0; } int chr_drv_open (struct inode *inode, struct file *filp) { printk("-------%s------- ",__FUNCTION__); return 0; } int chr_drv_release (struct inode *inode, struct file *filp) { printk("-------%s------- ",__FUNCTION__); return 0; } static struct file_operations file_ops={ .owner = THIS_MODULE, .open = chr_drv_open, .read = chr_drv_read, .write = chr_drv_write, .release = chr_drv_release, }; static int __init chr_io_init(void) { int ret; u32 value; printk("-----------%s-------- ",__FUNCTION__); //GFP_KERNEL 如果内存不够用函数会一直阻塞(休眠) led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL); if(led_dev == NULL) { //KERN_ERR 用于筛选调试信息 printk(KERN_ERR "malloc error! "); return -ENOMEM; } //动态分配设备节点 led_dev->dev_major = register_chrdev(0 , "led",&file_ops); if(led_dev->dev_major < 0) { printk(KERN_ERR "register_chrdev error! "); ret = -ENODEV; goto err_0; } led_dev->cls = class_create(THIS_MODULE, "led_cls"); if(IS_ERR(led_dev->cls))//用宏做出错判断 在这里出错了 之前的都要释放 { printk(KERN_ERR "class_create error! "); ret = PTR_ERR(led_dev->cls);//将指针出错的具体原因转换成出错码 goto err_1; } led_dev->dev = device_create(led_dev->cls, NULL, MKDEV(led_dev->dev_major,0), NULL, "led%d",0); if(IS_ERR(led_dev->dev)) { printk(KERN_ERR "device_create error! "); ret = PTR_ERR(led_dev->dev); goto err_2; } led_dev->reg_virt_base = ioremap(GPIOL2_CON, LEN); if(IS_ERR(led_dev->reg_virt_base)) { printk(KERN_ERR "ioremap error! "); ret = -ENOMEM; goto err_3; } //规范的输入输出方式 value = readl(led_dev->reg_virt_base); value &=~(1<<0); value |=1<<0; writel(value, led_dev->reg_virt_base); return 0; //梯形结构的错误处理 err_3: device_destroy(led_dev->cls , MKDEV(led_dev->dev_major,0)); err_2: class_destroy(led_dev->cls); err_1: unregister_chrdev(led_dev->dev_major, "led0"); //先释放设备号 再释放内存空间 err_0: kfree(led_dev);//防止在发生错误的时候以前申请的内存得不到释放 return ret; } static void __exit chr_io_exit(void) { printk("-----------%s-------- ",__FUNCTION__); iounmap(led_dev->reg_virt_base); unregister_chrdev(led_dev->dev_major, "led0"); device_destroy(led_dev->cls, MKDEV(led_dev->dev_major,0)); //和创建相反 class_destroy(led_dev->cls); } MODULE_LICENSE("GPL"); module_init(chr_io_init); module_exit(chr_io_exit);